public static void main(String[] args) throws Exception { // parse arguments if (args.length != 3 || !args[1].equals("-config")) { usage(); } // Determine type Type type = null; String software; if (args[0].equals("-w")) { type = Type.WELCOME; software = "TAISHI/WelcomeThreadServer"; } else if (args[0].equals("-b")) { type = Type.BUSY; software = "TAISHI/BusyWaitServer"; } else if (args[0].equals("-s")) { type = Type.SUSPENSION; software = "TAISHI/SuspensionServer"; } else { software = ""; usage(); } // Reading configuration file String config_file = args[2]; ReadConfig parse = new ReadConfig(config_file, software); HashMap<String, String> config_map = parse.getConfig(); Debug.debug(config_map.toString()); // check if config_map is valid // necessary to check the thread pool size is set if (config_map == null || !config_map.containsKey("ThreadPoolSize")) { throw new Exception("Incomplete configuration"); } // create cache Debug.debug("Create cache with size: " + config_map.get("CacheSize")); ThreadSafeCache cache = new ThreadSafeCache(Integer.parseInt(config_map.get("CacheSize"))); // create server ThreadPoolServer server = new ThreadPoolServer(type, config_map, cache); server.run(); }
// built via GTOTFGrammar construction from rtg rule and transducer rule public RGOTFGrammarRule(Symbol st, Vector<Symbol> c, double w) { super(); state = st; children = c; weight = w; counter++; if (counter % PERIOD == 0) { Debug.debug(true, "Built " + counter + " RGOTFGrammarRules"); } }
private void serveRequest(Socket connectionSocket) { try { GeneralRequestHandler handler = new GeneralRequestHandler(connectionSocket, config_map, cache); handler.processRequest(); } catch (Exception e) { Debug.debug(e.toString()); System.out.println("Exception happened in Thread " + this); } }
public ThreadPoolServer(Type type, HashMap<String, String> config_map, ThreadSafeCache cache) throws Exception { try { this.concurrent_type = type; this.config_map = config_map; this.cache = cache; // making welcome socket int port = Integer.parseInt(config_map.get("port")); welcomeSocket = new ServerSocket(port); this.thread_count = Integer.parseInt(config_map.get("ThreadPoolSize")); System.out.println( "Starting the server with thread pool size of " + thread_count + " and listening at: " + welcomeSocket); } catch (Exception e) { Debug.debug(e.getMessage()); throw new Exception("ThreadPool Server initial construction failed."); } }
// Run using thread pool competing on welcome socket private void run_welcome(WServiceThread[] threads) throws InterruptedException { for (int i = 0; i < threads.length; i++) { threads[i].join(); } Debug.debug("All threads finished. Exit"); }
/** * NEWT Utility class MainThread * * <p>This class provides a startup singleton <i>main thread</i>, from which a new thread with the * users main class is launched.<br> * Such behavior is necessary for native windowing toolkits, where the windowing management must * happen on the so called <i>main thread</i> e.g. for Mac OS X !<br> * Utilizing this class as a launchpad, now you are able to use a NEWT multithreaded application * with window handling within the different threads, even on these restricted platforms.<br> * To support your NEWT Window platform, you have to pass your <i>main thread</i> actions to {@link * #invoke invoke(..)}, have a look at the {@link com.jogamp.newt.macosx.MacWindow MacWindow} * implementation.<br> * <i>TODO</i>: Some hardcoded dependencies exist in this implementation, where you have to patch * this code or factor it out. * * <p>If your platform is not Mac OS X, but you want to test your code without modifying this class, * you have to set the system property <code>newt.MainThread.force</code> to <code>true</code>. * * <p>The code is compatible with all other platform, which support multithreaded windowing * handling. Since those platforms won't trigger the <i>main thread</i> serialization, the main * method will be simply executed, in case you haven't set <code>newt.MainThread.force</code> to * <code>true</code>. * * <p>Test case on Mac OS X (or any other platform): * * <PRE> * java -XstartOnFirstThread com.jogamp.newt.util.MainThread demos.es1.RedSquare -GL2 -GL2 -GL2 -GL2 * </PRE> * * Which starts 4 threads, each with a window and OpenGL rendering.<br> */ public class MainThread implements EDTUtil { private static AccessControlContext localACC = AccessController.getContext(); public static final boolean MAIN_THREAD_CRITERIA = (!NativeWindowFactory.isAWTAvailable() && NativeWindowFactory.TYPE_MACOSX.equals( NativeWindowFactory.getNativeWindowType(false))) || Debug.getBooleanProperty("newt.MainThread.force", true, localACC); protected static final boolean DEBUG = Debug.debug("MainThread"); private static MainThread singletonMainThread = new MainThread(); // one singleton MainThread private static boolean isExit = false; private static volatile boolean isRunning = false; private static Object taskWorkerLock = new Object(); private static boolean shouldStop; private static ArrayList tasks; private static Thread mainThread; private static Timer pumpMessagesTimer = null; private static TimerTask pumpMessagesTimerTask = null; private static Map /*<Display, Runnable>*/ pumpMessageDisplayMap = new HashMap(); private static boolean useMainThread = false; private static Class cAWTEventQueue = null; private static Method mAWTInvokeAndWait = null; private static Method mAWTInvokeLater = null; private static Method mAWTIsDispatchThread = null; static class MainAction extends Thread { private String mainClassName; private String[] mainClassArgs; private Class mainClass; private Method mainClassMain; public MainAction(String mainClassName, String[] mainClassArgs) { this.mainClassName = mainClassName; this.mainClassArgs = mainClassArgs; } public void run() { if (useMainThread) { // we have to start first to provide the service .. singletonMainThread.waitUntilRunning(); } // start user app .. try { Class mainClass = ReflectionUtil.getClass(mainClassName, true, getClass().getClassLoader()); if (null == mainClass) { throw new RuntimeException( new ClassNotFoundException("MainThread couldn't find main class " + mainClassName)); } try { mainClassMain = mainClass.getDeclaredMethod("main", new Class[] {String[].class}); mainClassMain.setAccessible(true); } catch (Throwable t) { throw new RuntimeException(t); } if (DEBUG) System.err.println( "MainAction.run(): " + Thread.currentThread().getName() + " invoke " + mainClassName); mainClassMain.invoke(null, new Object[] {mainClassArgs}); } catch (InvocationTargetException ite) { ite.getTargetException().printStackTrace(); } catch (Throwable t) { t.printStackTrace(); } if (DEBUG) System.err.println( "MainAction.run(): " + Thread.currentThread().getName() + " user app fin"); if (useMainThread) { singletonMainThread.stop(); if (DEBUG) System.err.println( "MainAction.run(): " + Thread.currentThread().getName() + " MainThread fin - stop"); System.exit(0); } } } private static MainAction mainAction; /** Your new java application main entry, which pipelines your application */ public static void main(String[] args) { useMainThread = MAIN_THREAD_CRITERIA; if (DEBUG) System.err.println( "MainThread.main(): " + Thread.currentThread().getName() + " useMainThread " + useMainThread); if (args.length == 0) { return; } String mainClassName = args[0]; String[] mainClassArgs = new String[args.length - 1]; if (args.length > 1) { System.arraycopy(args, 1, mainClassArgs, 0, args.length - 1); } NEWTJNILibLoader.loadNEWT(); mainAction = new MainAction(mainClassName, mainClassArgs); if (NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false))) { ReflectionUtil.callStaticMethod( "com.jogamp.newt.impl.macosx.MacDisplay", "initSingleton", null, null, MainThread.class.getClassLoader()); } if (useMainThread) { shouldStop = false; tasks = new ArrayList(); mainThread = Thread.currentThread(); // dispatch user's main thread .. mainAction.start(); // do our main thread task scheduling singletonMainThread.run(); } else { // run user's main in this thread mainAction.run(); } } public static final MainThread getSingleton() { return singletonMainThread; } public static Runnable removePumpMessage(Display dpy) { synchronized (pumpMessageDisplayMap) { return (Runnable) pumpMessageDisplayMap.remove(dpy); } } public static void addPumpMessage(Display dpy, Runnable pumpMessage) { if (useMainThread) { return; // error ? } if (null == pumpMessagesTimer) { synchronized (MainThread.class) { if (null == pumpMessagesTimer) { pumpMessagesTimer = new Timer(); pumpMessagesTimerTask = new TimerTask() { public void run() { synchronized (pumpMessageDisplayMap) { for (Iterator i = pumpMessageDisplayMap.values().iterator(); i.hasNext(); ) { ((Runnable) i.next()).run(); } } } }; pumpMessagesTimer.scheduleAtFixedRate( pumpMessagesTimerTask, 0, defaultEDTPollGranularity); } } } synchronized (pumpMessageDisplayMap) { pumpMessageDisplayMap.put(dpy, pumpMessage); } } private void initAWTReflection() { if (null == cAWTEventQueue) { ClassLoader cl = MainThread.class.getClassLoader(); cAWTEventQueue = ReflectionUtil.getClass("java.awt.EventQueue", true, cl); mAWTInvokeAndWait = ReflectionUtil.getMethod( cAWTEventQueue, "invokeAndWait", new Class[] {java.lang.Runnable.class}, cl); mAWTInvokeLater = ReflectionUtil.getMethod( cAWTEventQueue, "invokeLater", new Class[] {java.lang.Runnable.class}, cl); mAWTIsDispatchThread = ReflectionUtil.getMethod(cAWTEventQueue, "isDispatchThread", new Class[] {}, cl); } } public void start() { // nop } public void stop() { if (DEBUG) System.err.println("MainThread.stop(): " + Thread.currentThread().getName() + " start"); synchronized (taskWorkerLock) { if (isRunning) { shouldStop = true; } taskWorkerLock.notifyAll(); } if (DEBUG) System.err.println("MainThread.stop(): " + Thread.currentThread().getName() + " end"); } public boolean isCurrentThreadEDT() { if (NativeWindowFactory.isAWTAvailable()) { initAWTReflection(); return ((Boolean) ReflectionUtil.callMethod(null, mAWTIsDispatchThread, null)).booleanValue(); } return isRunning() && mainThread == Thread.currentThread(); } public boolean isRunning() { if (useMainThread) { synchronized (taskWorkerLock) { return isRunning; } } return true; // AWT is always running } private void invokeLater(Runnable task) { synchronized (taskWorkerLock) { if (isRunning() && mainThread != Thread.currentThread()) { tasks.add(task); taskWorkerLock.notifyAll(); } else { // if !running or isEDTThread, do it right away task.run(); } } } /** invokes the given Runnable */ public void invoke(boolean wait, Runnable r) { if (r == null) { return; } if (NativeWindowFactory.isAWTAvailable()) { initAWTReflection(); // handover to AWT MainThread .. try { if (((Boolean) ReflectionUtil.callMethod(null, mAWTIsDispatchThread, null)) .booleanValue()) { r.run(); return; } if (wait) { ReflectionUtil.callMethod(null, mAWTInvokeAndWait, new Object[] {r}); } else { ReflectionUtil.callMethod(null, mAWTInvokeLater, new Object[] {r}); } } catch (Exception e) { throw new NativeWindowException(e); } return; } // if this main thread is not being used or // if this is already the main thread .. just execute. if (!isRunning() || mainThread == Thread.currentThread()) { r.run(); return; } boolean doWait = wait && isRunning() && mainThread != Thread.currentThread(); Object lock = new Object(); RunnableTask rTask = new RunnableTask(r, doWait ? lock : null, true); Throwable throwable = null; synchronized (lock) { invokeLater(rTask); if (doWait) { try { lock.wait(); } catch (InterruptedException ie) { throwable = ie; } } } if (null == throwable) { throwable = rTask.getThrowable(); } if (null != throwable) { throw new RuntimeException(throwable); } } public void waitUntilIdle() {} public void waitUntilStopped() {} private void waitUntilRunning() { synchronized (taskWorkerLock) { if (isExit) return; while (!isRunning) { try { taskWorkerLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public void run() { if (DEBUG) System.err.println("MainThread.run(): " + Thread.currentThread().getName()); synchronized (taskWorkerLock) { isRunning = true; taskWorkerLock.notifyAll(); } while (!shouldStop) { try { // wait for something todo .. synchronized (taskWorkerLock) { while (!shouldStop && tasks.size() == 0) { try { taskWorkerLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // take over the tasks .. if (!shouldStop && tasks.size() > 0) { Runnable task = (Runnable) tasks.remove(0); task.run(); // FIXME: could be run outside of lock } taskWorkerLock.notifyAll(); } } catch (Throwable t) { // handle errors .. t.printStackTrace(); } finally { // epilog - unlock locked stuff } } if (DEBUG) System.err.println("MainThread.run(): " + Thread.currentThread().getName() + " fin"); synchronized (taskWorkerLock) { isRunning = false; isExit = true; taskWorkerLock.notifyAll(); } } }
public abstract class GLDrawableImpl implements GLDrawable { protected static final boolean DEBUG = Debug.debug("GLDrawable"); protected GLDrawableImpl(GLDrawableFactory factory, NativeSurface comp, boolean realized) { this.factory = factory; this.surface = comp; this.realized = realized; this.requestedCapabilities = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getRequestedCapabilities(); } /** Returns the DynamicLookupHelper */ public abstract GLDynamicLookupHelper getGLDynamicLookupHelper(); public GLDrawableFactoryImpl getFactoryImpl() { return (GLDrawableFactoryImpl) getFactory(); } /** * For offscreen GLDrawables (pbuffers and "pixmap" drawables), indicates that native resources * should be reclaimed. */ public void destroy() { surface.getGraphicsConfiguration().getScreen().getDevice().lock(); try { destroyImpl(); } finally { surface.getGraphicsConfiguration().getScreen().getDevice().unlock(); } } protected void destroyImpl() { throw new GLException("Should not call this (should only be called for offscreen GLDrawables)"); } public final void swapBuffers() throws GLException { GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); if (caps.getDoubleBuffered()) { if (!surface.surfaceSwap()) { int lockRes = lockSurface(); // it's recursive, so it's ok within [makeCurrent .. release] if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { return; } try { if (NativeSurface.LOCK_SURFACE_CHANGED == lockRes) { updateHandle(); } swapBuffersImpl(); } finally { unlockSurface(); } } } else { GLContext ctx = GLContext.getCurrent(); if (null != ctx && ctx.getGLDrawable() == this) { ctx.getGL().glFinish(); } } surface.surfaceUpdated(this, surface, System.currentTimeMillis()); } protected abstract void swapBuffersImpl(); public static String toHexString(long hex) { return "0x" + Long.toHexString(hex); } public GLProfile getGLProfile() { return requestedCapabilities.getGLProfile(); } public GLCapabilitiesImmutable getChosenGLCapabilities() { return (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); } public GLCapabilitiesImmutable getRequestedGLCapabilities() { return requestedCapabilities; } public NativeSurface getNativeSurface() { return surface; } /** called with locked surface @ setRealized(false) */ protected void destroyHandle() {} /** called with locked surface @ setRealized(true) or @ lockSurface(..) when surface changed */ protected void updateHandle() {} public long getHandle() { return surface.getSurfaceHandle(); } public GLDrawableFactory getFactory() { return factory; } public final synchronized void setRealized(boolean realizedArg) { if (realized != realizedArg) { if (DEBUG) { System.err.println( "setRealized: " + getClass().getName() + " " + realized + " -> " + realizedArg); } realized = realizedArg; AbstractGraphicsDevice aDevice = surface.getGraphicsConfiguration().getScreen().getDevice(); if (realizedArg) { if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockSurface()) { throw new GLException( "GLDrawableImpl.setRealized(true): already realized, but surface not ready (lockSurface)"); } } else { aDevice.lock(); } try { setRealizedImpl(); if (realizedArg) { updateHandle(); } else { destroyHandle(); } } finally { if (realizedArg) { unlockSurface(); } else { aDevice.unlock(); } } } else if (DEBUG) { System.err.println( "setRealized: " + getClass().getName() + " " + this.realized + " == " + realizedArg); } } protected abstract void setRealizedImpl(); public synchronized boolean isRealized() { return realized; } public int getWidth() { return surface.getWidth(); } public int getHeight() { return surface.getHeight(); } public int lockSurface() throws GLException { return surface.lockSurface(); } public void unlockSurface() { surface.unlockSurface(); } public boolean isSurfaceLocked() { return surface.isSurfaceLocked(); } public String toString() { return getClass().getSimpleName() + "[Realized " + isRealized() + ",\n\tFactory " + getFactory() + ",\n\thandle " + toHexString(getHandle()) + ",\n\tWindow " + getNativeSurface() + "]"; } protected GLDrawableFactory factory; protected NativeSurface surface; protected GLCapabilitiesImmutable requestedCapabilities; // Indicates whether the surface (if an onscreen context) has been // realized. Plausibly, before the surface is realized the JAWT // should return an error or NULL object from some of its // operations; this appears to be the case on Win32 but is not true // at least with Sun's current X11 implementation (1.4.x), which // crashes with no other error reported if the DrawingSurfaceInfo is // fetched from a locked DrawingSurface during the validation as a // result of calling show() on the main thread. To work around this // we prevent any JAWT or OpenGL operations from being done until // addNotify() is called on the surface. protected boolean realized; }
/** * Subclass of GraphicsConfigurationFactory used when non-AWT tookits 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 GraphicsConfigurationFactory { protected static final boolean DEBUG = Debug.debug("GraphicsConfiguration"); public X11GLXGraphicsConfigurationFactory() { GraphicsConfigurationFactory.registerFactory( javax.media.nativewindow.x11.X11GraphicsDevice.class, this); } public AbstractGraphicsConfiguration chooseGraphicsConfiguration( Capabilities capabilities, CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { return chooseGraphicsConfigurationStatic(capabilities, chooser, absScreen); } protected static X11GLXGraphicsConfiguration createDefaultGraphicsConfiguration( AbstractGraphicsScreen absScreen, boolean onscreen, boolean usePBuffer) { if (absScreen == null) { throw new IllegalArgumentException("AbstractGraphicsScreen is null"); } if (!(absScreen instanceof X11GraphicsScreen)) { throw new IllegalArgumentException("Only X11GraphicsScreen are allowed here"); } X11GraphicsScreen x11Screen = (X11GraphicsScreen) absScreen; GLProfile glProfile = GLProfile.getDefault(); GLCapabilities caps = null; XVisualInfo xvis = null; long fbcfg = 0; int fbid = -1; // Utilizing FBConfig // GLCapabilities capsFB = null; long display = x11Screen.getDevice().getHandle(); try { NativeWindowFactory.getDefaultFactory().getToolkitLock().lock(); X11Lib.XLockDisplay(display); int screen = x11Screen.getIndex(); boolean isMultisampleAvailable = GLXUtil.isMultisampleAvailable(display); long visID = X11Lib.DefaultVisualID(display, x11Screen.getIndex()); xvis = X11GLXGraphicsConfiguration.XVisualID2XVisualInfo(display, visID); caps = X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities( glProfile, display, xvis, onscreen, usePBuffer, isMultisampleAvailable); int[] attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList( caps, true, isMultisampleAvailable, display, screen); int[] count = {-1}; PointerBuffer fbcfgsL = GLX.glXChooseFBConfigCopied(display, screen, attribs, 0, count, 0); if (fbcfgsL == null || fbcfgsL.limit() < 1) { throw new Exception("Could not fetch FBConfig for " + caps); } fbcfg = fbcfgsL.get(0); capsFB = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities( glProfile, display, fbcfg, true, onscreen, usePBuffer, isMultisampleAvailable); fbid = X11GLXGraphicsConfiguration.glXFBConfig2FBConfigID(display, fbcfg); xvis = GLX.glXGetVisualFromFBConfigCopied(display, fbcfg); if (xvis == null) { throw new GLException("Error: Choosen FBConfig has no visual"); } } catch (Throwable t) { } finally { X11Lib.XUnlockDisplay(display); NativeWindowFactory.getDefaultFactory().getToolkitLock().unlock(); } return new X11GLXGraphicsConfiguration( x11Screen, (null != capsFB) ? capsFB : caps, caps, null, xvis, fbcfg, fbid); } protected static X11GLXGraphicsConfiguration chooseGraphicsConfigurationStatic( Capabilities capabilities, CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { if (absScreen == null) { throw new IllegalArgumentException("AbstractGraphicsScreen is null"); } if (!(absScreen instanceof X11GraphicsScreen)) { throw new IllegalArgumentException("Only X11GraphicsScreen are allowed here"); } X11GraphicsScreen x11Screen = (X11GraphicsScreen) absScreen; if (capabilities != null && !(capabilities instanceof GLCapabilities)) { throw new IllegalArgumentException( "This NativeWindowFactory accepts only GLCapabilities objects"); } if (chooser != null && !(chooser instanceof GLCapabilitiesChooser)) { throw new IllegalArgumentException( "This NativeWindowFactory accepts only GLCapabilitiesChooser objects"); } if (capabilities == null) { capabilities = new GLCapabilities(null); } boolean onscreen = capabilities.isOnscreen(); boolean usePBuffer = ((GLCapabilities) capabilities).isPBuffer(); GLCapabilities caps2 = (GLCapabilities) capabilities.clone(); if (!caps2.isOnscreen()) { // OFFSCREEN !DOUBLE_BUFFER // FIXME DBLBUFOFFSCRN caps2.setDoubleBuffered(false); } X11GLXGraphicsConfiguration res; res = chooseGraphicsConfigurationFBConfig( (GLCapabilities) caps2, (GLCapabilitiesChooser) chooser, x11Screen); if (null == res) { if (usePBuffer) { throw new GLException( "Error: Couldn't create X11GLXGraphicsConfiguration based on FBConfig"); } res = chooseGraphicsConfigurationXVisual( (GLCapabilities) caps2, (GLCapabilitiesChooser) chooser, x11Screen); } if (null == res) { throw new GLException("Error: Couldn't create X11GLXGraphicsConfiguration"); } if (DEBUG) { System.err.println( "X11GLXGraphicsConfiguration.chooseGraphicsConfigurationStatic(" + x11Screen + "," + caps2 + "): " + res); } return res; } protected static X11GLXGraphicsConfiguration chooseGraphicsConfigurationFBConfig( GLCapabilities capabilities, GLCapabilitiesChooser chooser, X11GraphicsScreen x11Screen) { int recommendedIndex = -1; GLCapabilities[] caps = null; PointerBuffer fbcfgsL = null; int chosen = -1; int retFBID = -1; XVisualInfo retXVisualInfo = null; GLProfile glProfile = capabilities.getGLProfile(); boolean onscreen = capabilities.isOnscreen(); boolean usePBuffer = capabilities.isPBuffer(); // Utilizing FBConfig // AbstractGraphicsDevice absDevice = x11Screen.getDevice(); long display = absDevice.getHandle(); try { NativeWindowFactory.getDefaultFactory().getToolkitLock().lock(); X11Lib.XLockDisplay(display); int screen = x11Screen.getIndex(); boolean isMultisampleAvailable = GLXUtil.isMultisampleAvailable(display); int[] attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList( capabilities, true, isMultisampleAvailable, display, screen); int[] count = {-1}; fbcfgsL = GLX.glXChooseFBConfigCopied(display, screen, attribs, 0, count, 0); if (fbcfgsL == null || fbcfgsL.limit() < 1) { if (DEBUG) { System.err.println( "X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: Failed glXChooseFBConfig (" + x11Screen + "," + capabilities + "): " + fbcfgsL + ", " + count[0]); } return null; } if (!X11GLXGraphicsConfiguration.GLXFBConfigValid(display, fbcfgsL.get(0))) { if (DEBUG) { System.err.println( "X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: Failed - GLX FBConfig invalid: (" + x11Screen + "," + capabilities + "): " + fbcfgsL + ", fbcfg: 0x" + Long.toHexString(fbcfgsL.get(0))); } return null; } recommendedIndex = 0; // 1st match is always recommended .. caps = new GLCapabilities[fbcfgsL.limit()]; for (int i = 0; i < fbcfgsL.limit(); i++) { caps[i] = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities( glProfile, display, fbcfgsL.get(i), false, onscreen, usePBuffer, isMultisampleAvailable); } if (null == chooser) { chosen = recommendedIndex; } else { try { chosen = chooser.chooseCapabilities(capabilities, caps, recommendedIndex); } catch (NativeWindowException e) { if (DEBUG) { e.printStackTrace(); } chosen = -1; } } if (chosen < 0) { // keep on going .. if (DEBUG) { System.err.println( "X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig Failed .. unable to choose config, using first"); } chosen = 0; // default .. } else if (chosen >= caps.length) { throw new GLException( "GLCapabilitiesChooser specified invalid index (expected 0.." + (caps.length - 1) + ")"); } retFBID = X11GLXGraphicsConfiguration.glXFBConfig2FBConfigID(display, fbcfgsL.get(chosen)); retXVisualInfo = GLX.glXGetVisualFromFBConfigCopied(display, fbcfgsL.get(chosen)); if (retXVisualInfo == null) { if (DEBUG) { System.err.println( "X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: Failed glXGetVisualFromFBConfig (" + x11Screen + ", " + fbcfgsL.get(chosen) + " (Continue: " + (false == caps[chosen].isOnscreen()) + "):\n\t" + caps[chosen]); } if (caps[chosen].isOnscreen()) { // Onscreen drawables shall have a XVisual .. return null; } } } finally { X11Lib.XUnlockDisplay(display); NativeWindowFactory.getDefaultFactory().getToolkitLock().unlock(); } return new X11GLXGraphicsConfiguration( x11Screen, caps[chosen], capabilities, chooser, retXVisualInfo, fbcfgsL.get(chosen), retFBID); } protected static X11GLXGraphicsConfiguration chooseGraphicsConfigurationXVisual( GLCapabilities capabilities, GLCapabilitiesChooser chooser, X11GraphicsScreen x11Screen) { if (chooser == null) { chooser = new DefaultGLCapabilitiesChooser(); } // Until we have a rock-solid visual selection algorithm written // in pure Java, we're going to provide the underlying window // system's selection to the chooser as a hint GLProfile glProfile = capabilities.getGLProfile(); boolean onscreen = capabilities.isOnscreen(); GLCapabilities[] caps = null; int recommendedIndex = -1; XVisualInfo retXVisualInfo = null; int chosen = -1; AbstractGraphicsDevice absDevice = x11Screen.getDevice(); long display = absDevice.getHandle(); try { NativeWindowFactory.getDefaultFactory().getToolkitLock().lock(); X11Lib.XLockDisplay(display); int screen = x11Screen.getIndex(); boolean isMultisampleAvailable = GLXUtil.isMultisampleAvailable(display); int[] attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList( capabilities, false, isMultisampleAvailable, display, screen); XVisualInfo[] infos = null; XVisualInfo recommendedVis = GLX.glXChooseVisualCopied(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 0x" + Long.toHexString(recommendedVis.getVisualid())); } } int[] count = new int[1]; XVisualInfo template = XVisualInfo.create(); template.setScreen(screen); infos = X11Lib.XGetVisualInfoCopied(display, X11Lib.VisualScreenMask, template, count, 0); if (infos == null || infos.length < 1) { throw new GLException("Error while enumerating available XVisualInfos"); } caps = new GLCapabilities[infos.length]; for (int i = 0; i < infos.length; i++) { caps[i] = X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities( glProfile, display, infos[i], onscreen, false, isMultisampleAvailable); // Attempt to find the visual chosen by glXChooseVisual if (recommendedVis != null && recommendedVis.getVisualid() == infos[i].getVisualid()) { recommendedIndex = i; } } try { chosen = chooser.chooseCapabilities(capabilities, caps, recommendedIndex); } catch (NativeWindowException e) { if (DEBUG) { e.printStackTrace(); } chosen = -1; } if (chosen < 0) { // keep on going .. if (DEBUG) { System.err.println( "X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual Failed .. unable to choose config, using first"); } chosen = 0; // default .. } else if (chosen >= caps.length) { throw new GLException( "GLCapabilitiesChooser specified invalid index (expected 0.." + (caps.length - 1) + ")"); } if (infos[chosen] == null) { throw new GLException("GLCapabilitiesChooser chose an invalid visual"); } retXVisualInfo = XVisualInfo.create(infos[chosen]); } finally { X11Lib.XUnlockDisplay(display); NativeWindowFactory.getDefaultFactory().getToolkitLock().unlock(); } return new X11GLXGraphicsConfiguration( x11Screen, caps[chosen], capabilities, chooser, retXVisualInfo, 0, -1); } }
/** * Represents an OpenGL texture object. Contains convenience routines for enabling/disabling OpenGL * texture state, binding this texture, and computing texture coordinates for both the entire image * as well as a sub-image. * * <p><a name="nonpow2"><b>Non-power-of-two restrictions</b></a> <br> * When creating an OpenGL texture object, the Texture class will attempt to leverage the <a * href="http://www.opengl.org/registry/specs/ARB/texture_non_power_of_two.txt">GL_ARB_texture_non_power_of_two</a> * and <a * href="http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt">GL_ARB_texture_rectangle</a> * extensions (in that order) whenever possible. If neither extension is available, the Texture * class will simply upload a non-pow2-sized image into a standard pow2-sized texture (without any * special scaling). Since the choice of extension (or whether one is used at all) depends on the * user's machine configuration, developers are recommended to use {@link #getImageTexCoords} and * {@link #getSubImageTexCoords}, as those methods will calculate the appropriate texture * coordinates for the situation. * * <p>One caveat in this approach is that certain texture wrap modes (e.g. <code>GL_REPEAT</code>) * are not legal when the GL_ARB_texture_rectangle extension is in use. Another issue to be aware of * is that in the default pow2 scenario, if the original image does not have pow2 dimensions, then * wrapping may not work as one might expect since the image does not extend to the edges of the * pow2 texture. If texture wrapping is important, it is recommended to use only pow2-sized images * with the Texture class. * * <p><a name="perftips"><b>Performance Tips</b></a> <br> * For best performance, try to avoid calling {@link #enable} / {@link #bind} / {@link #disable} any * more than necessary. For example, applications using many Texture objects in the same scene may * want to reduce the number of calls to both {@link #enable} and {@link #disable}. To do this it is * necessary to call {@link #getTarget} to make sure the OpenGL texture target is the same for all * of the Texture objects in use; non-power-of-two textures using the GL_ARB_texture_rectangle * extension use a different target than power-of-two textures using the GL_TEXTURE_2D target. Note * that when switching between textures it is necessary to call {@link #bind}, but when drawing many * triangles all using the same texture, for best performance only one call to {@link #bind} should * be made. * * <p><a name="premult"><b>Alpha premultiplication and blending</b></a> <br> * The mathematically correct way to perform blending in OpenGL (with the SrcOver "source over * destination" mode, or any other Porter-Duff rule) is to use "premultiplied color components", * which means the R/G/ B color components have already been multiplied by the alpha value. To make * things easier for developers, the Texture class will automatically convert non-premultiplied * image data into premultiplied data when storing it into an OpenGL texture. As a result, it is * important to use the correct blending function; for example, the SrcOver rule is expressed as: * * <pre> * gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); * </pre> * * Also, when using a texture function like <code>GL_MODULATE</code> where the current color plays a * role, it is important to remember to make sure that the color is specified in a premultiplied * form, for example: * * <pre> * float a = ...; * float r = r * a; * float g = g * a; * float b = b * a; * gl.glColor4f(r, g, b, a); * </pre> * * For reference, here is a list of the Porter-Duff compositing rules and the associated OpenGL * blend functions (source and destination factors) to use in the face of premultiplied alpha: * * <p><CENTER> * * <TABLE WIDTH="75%"> * <TR> <TD> Rule <TD> Source <TD> Dest * <TR> <TD> Clear <TD> GL_ZERO <TD> GL_ZERO * <TR> <TD> Src <TD> GL_ONE <TD> GL_ZERO * <TR> <TD> SrcOver <TD> GL_ONE <TD> GL_ONE_MINUS_SRC_ALPHA * <TR> <TD> DstOver <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_ONE * <TR> <TD> SrcIn <TD> GL_DST_ALPHA <TD> GL_ZERO * <TR> <TD> DstIn <TD> GL_ZERO <TD> GL_SRC_ALPHA * <TR> <TD> SrcOut <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_ZERO * <TR> <TD> DstOut <TD> GL_ZERO <TD> GL_ONE_MINUS_SRC_ALPHA * <TR> <TD> Dst <TD> GL_ZERO <TD> GL_ONE * <TR> <TD> SrcAtop <TD> GL_DST_ALPHA <TD> GL_ONE_MINUS_SRC_ALPHA * <TR> <TD> DstAtop <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_SRC_ALPHA * <TR> <TD> AlphaXor <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_ONE_MINUS_SRC_ALPHA * </TABLE> * * </CENTER> * * @author Chris Campbell * @author Kenneth Russell */ public class Texture { /** The GL target type. */ private int target; /** The GL texture ID. */ private int texID; /** The width of the texture. */ private int texWidth; /** The height of the texture. */ private int texHeight; /** The width of the image. */ private int imgWidth; /** The height of the image. */ private int imgHeight; /** * The original aspect ratio of the image, before any rescaling that might have occurred due to * using the GLU mipmap routines. */ private float aspectRatio; /** Indicates whether the TextureData requires a vertical flip of the texture coords. */ private boolean mustFlipVertically; /** Indicates whether we're using automatic mipmap generation support (GL_GENERATE_MIPMAP). */ private boolean usingAutoMipmapGeneration; /** The texture coordinates corresponding to the entire image. */ private TextureCoords coords; /** An estimate of the amount of texture memory this texture consumes. */ private int estimatedMemorySize; private static final boolean DEBUG = Debug.debug("Texture"); private static final boolean VERBOSE = Debug.verbose(); // For testing alternate code paths on more capable hardware private static final boolean disableNPOT = Debug.isPropertyDefined("jogl.texture.nonpot"); private static final boolean disableTexRect = Debug.isPropertyDefined("jogl.texture.notexrect"); // For now make Texture constructor package-private to limit the // number of public APIs we commit to Texture(TextureData data) throws GLException { GL gl = GLU.getCurrentGL(); texID = createTextureID(gl); updateImage(data); } // Constructor for use when creating e.g. cube maps, where there is // no initial texture data Texture(int target) throws GLException { GL gl = GLU.getCurrentGL(); texID = createTextureID(gl); this.target = target; } /** * Enables this texture's target (e.g., GL_TEXTURE_2D) in the current GL context's state. This * method is a shorthand equivalent of the following OpenGL code: * * <pre> * gl.glEnable(texture.getTarget()); * </pre> * * See the <a href="#perftips">performance tips</a> above for hints on how to maximize performance * when using many Texture objects. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void enable() throws GLException { GLU.getCurrentGL().glEnable(target); } /** * Disables this texture's target (e.g., GL_TEXTURE_2D) in the current GL context's state. This * method is a shorthand equivalent of the following OpenGL code: * * <pre> * gl.glDisable(texture.getTarget()); * </pre> * * See the <a href="#perftips">performance tips</a> above for hints on how to maximize performance * when using many Texture objects. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void disable() throws GLException { GLU.getCurrentGL().glDisable(target); } /** * Binds this texture to the current GL context. This method is a shorthand equivalent of the * following OpenGL code: * * <pre> * gl.glBindTexture(texture.getTarget(), texture.getTextureObject()); * </pre> * * See the <a href="#perftips">performance tips</a> above for hints on how to maximize performance * when using many Texture objects. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void bind() throws GLException { GLU.getCurrentGL().glBindTexture(target, texID); } /** * Disposes the native resources used by this texture object. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void dispose() throws GLException { GLU.getCurrentGL().glDeleteTextures(1, new int[] {texID}, 0); texID = 0; } /** * Returns the OpenGL "target" of this texture. * * @return the OpenGL target of this texture * @see javax.media.opengl.GL#GL_TEXTURE_2D * @see javax.media.opengl.GL#GL_TEXTURE_RECTANGLE_ARB */ public int getTarget() { return target; } /** * Returns the width of the allocated OpenGL texture in pixels. Note that the texture width will * be greater than or equal to the width of the image contained within. * * @return the width of the texture */ public int getWidth() { return texWidth; } /** * Returns the height of the allocated OpenGL texture in pixels. Note that the texture height will * be greater than or equal to the height of the image contained within. * * @return the height of the texture */ public int getHeight() { return texHeight; } /** * Returns the width of the image contained within this texture. Note that for non-power-of-two * textures in particular this may not be equal to the result of {@link #getWidth}. It is * recommended that applications call {@link #getImageTexCoords} and {@link #getSubImageTexCoords} * rather than using this API directly. * * @return the width of the image */ public int getImageWidth() { return imgWidth; } /** * Returns the height of the image contained within this texture. Note that for non-power-of-two * textures in particular this may not be equal to the result of {@link #getHeight}. It is * recommended that applications call {@link #getImageTexCoords} and {@link #getSubImageTexCoords} * rather than using this API directly. * * @return the height of the image */ public int getImageHeight() { return imgHeight; } /** * Returns the original aspect ratio of the image, defined as (image width) / (image height), * before any scaling that might have occurred as a result of using the GLU mipmap routines. */ public float getAspectRatio() { return aspectRatio; } /** * Returns the set of texture coordinates corresponding to the entire image. If the TextureData * indicated that the texture coordinates must be flipped vertically, the returned TextureCoords * will take that into account. * * @return the texture coordinates corresponding to the entire image */ public TextureCoords getImageTexCoords() { return coords; } /** * Returns the set of texture coordinates corresponding to the specified sub-image. The (x1, y1) * and (x2, y2) points are specified in terms of pixels starting from the lower-left of the image. * (x1, y1) should specify the lower-left corner of the sub-image and (x2, y2) the upper-right * corner of the sub-image. If the TextureData indicated that the texture coordinates must be * flipped vertically, the returned TextureCoords will take that into account; this should not be * handled by the end user in the specification of the y1 and y2 coordinates. * * @return the texture coordinates corresponding to the specified sub-image */ public TextureCoords getSubImageTexCoords(int x1, int y1, int x2, int y2) { if (target == GL.GL_TEXTURE_RECTANGLE_ARB) { if (mustFlipVertically) { return new TextureCoords(x1, texHeight - y1, x2, texHeight - y2); } else { return new TextureCoords(x1, y1, x2, y2); } } else { float tx1 = (float) x1 / (float) texWidth; float ty1 = (float) y1 / (float) texHeight; float tx2 = (float) x2 / (float) texWidth; float ty2 = (float) y2 / (float) texHeight; if (mustFlipVertically) { float yMax = (float) imgHeight / (float) texHeight; return new TextureCoords(tx1, yMax - ty1, tx2, yMax - ty2); } else { return new TextureCoords(tx1, ty1, tx2, ty2); } } } /** * Updates the entire content area of this texture using the data in the given image. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void updateImage(TextureData data) throws GLException { updateImage(data, 0); } /** * Indicates whether this texture's texture coordinates must be flipped vertically in order to * properly display the texture. This is handled automatically by {@link #getImageTexCoords * getImageTexCoords} and {@link #getSubImageTexCoords getSubImageTexCoords}, but applications may * generate or otherwise produce texture coordinates which must be corrected. */ public boolean getMustFlipVertically() { return mustFlipVertically; } /** * Updates the content area of the specified target of this texture using the data in the given * image. In general this is intended for construction of cube maps. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void updateImage(TextureData data, int target) throws GLException { GL gl = GLU.getCurrentGL(); imgWidth = data.getWidth(); imgHeight = data.getHeight(); aspectRatio = (float) imgWidth / (float) imgHeight; mustFlipVertically = data.getMustFlipVertically(); int texTarget = 0; int texParamTarget = this.target; // See whether we have automatic mipmap generation support boolean haveAutoMipmapGeneration = (gl.isExtensionAvailable("GL_VERSION_1_4") || gl.isExtensionAvailable("GL_SGIS_generate_mipmap")); // Indicate to the TextureData what functionality is available data.setHaveEXTABGR(gl.isExtensionAvailable("GL_EXT_abgr")); data.setHaveGL12(gl.isExtensionAvailable("GL_VERSION_1_2")); // Note that automatic mipmap generation doesn't work for // GL_ARB_texture_rectangle if ((!isPowerOfTwo(imgWidth) || !isPowerOfTwo(imgHeight)) && !haveNPOT(gl)) { haveAutoMipmapGeneration = false; } boolean expandingCompressedTexture = false; if (data.getMipmap() && !haveAutoMipmapGeneration) { // GLU always scales the texture's dimensions to be powers of // two. It also doesn't really matter exactly what the texture // width and height are because the texture coords are always // between 0.0 and 1.0. imgWidth = nextPowerOfTwo(imgWidth); imgHeight = nextPowerOfTwo(imgHeight); texWidth = imgWidth; texHeight = imgHeight; texTarget = GL.GL_TEXTURE_2D; } else if ((isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight)) || haveNPOT(gl)) { if (DEBUG) { if (isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight)) { System.err.println("Power-of-two texture"); } else { System.err.println("Using GL_ARB_texture_non_power_of_two"); } } texWidth = imgWidth; texHeight = imgHeight; texTarget = GL.GL_TEXTURE_2D; } else if (haveTexRect(gl) && !data.isDataCompressed()) { // GL_ARB_texture_rectangle does not work for compressed textures if (DEBUG) { System.err.println("Using GL_ARB_texture_rectangle"); } texWidth = imgWidth; texHeight = imgHeight; texTarget = GL.GL_TEXTURE_RECTANGLE_ARB; } else { // If we receive non-power-of-two compressed texture data and // don't have true hardware support for compressed textures, we // can fake this support by producing an empty "compressed" // texture image, using glCompressedTexImage2D with that to // allocate the texture, and glCompressedTexSubImage2D with the // incoming data. if (data.isDataCompressed()) { if (data.getMipmapData() != null) { // We don't currently support expanding of compressed, // mipmapped non-power-of-two textures to the nearest power // of two; the obvious port of the non-mipmapped code didn't // work throw new GLException( "Mipmapped non-power-of-two compressed textures only supported on OpenGL 2.0 hardware (GL_ARB_texture_non_power_of_two)"); } expandingCompressedTexture = true; } if (DEBUG) { System.err.println("Expanding texture to power-of-two dimensions"); } if (data.getBorder() != 0) { throw new RuntimeException( "Scaling up a non-power-of-two texture which has a border won't work"); } texWidth = nextPowerOfTwo(imgWidth); texHeight = nextPowerOfTwo(imgHeight); texTarget = GL.GL_TEXTURE_2D; } texParamTarget = texTarget; setImageSize(imgWidth, imgHeight, texTarget); if (target != 0) { // Allow user to override auto detection and skip bind step (for // cubemap construction) texTarget = target; if (this.target == 0) { throw new GLException("Override of target failed; no target specified yet"); } texParamTarget = this.target; gl.glBindTexture(texParamTarget, texID); } else { gl.glBindTexture(texTarget, texID); } if (data.getMipmap() && !haveAutoMipmapGeneration) { int[] align = new int[1]; gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment()); if (data.isDataCompressed()) { throw new GLException("May not request mipmap generation for compressed textures"); } try { GLU glu = new GLU(); glu.gluBuild2DMipmaps( texTarget, data.getInternalFormat(), data.getWidth(), data.getHeight(), data.getPixelFormat(), data.getPixelType(), data.getBuffer()); } finally { gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment } } else { checkCompressedTextureExtensions(data); Buffer[] mipmapData = data.getMipmapData(); if (mipmapData != null) { int width = texWidth; int height = texHeight; for (int i = 0; i < mipmapData.length; i++) { if (data.isDataCompressed()) { // Need to use glCompressedTexImage2D directly to allocate and fill this image // Avoid spurious memory allocation when possible gl.glCompressedTexImage2D( texTarget, i, data.getInternalFormat(), width, height, data.getBorder(), mipmapData[i].remaining(), mipmapData[i]); } else { // Allocate texture image at this level gl.glTexImage2D( texTarget, i, data.getInternalFormat(), width, height, data.getBorder(), data.getPixelFormat(), data.getPixelType(), null); updateSubImageImpl(data, texTarget, i, 0, 0, 0, 0, data.getWidth(), data.getHeight()); } width = Math.max(width / 2, 1); height = Math.max(height / 2, 1); } } else { if (data.isDataCompressed()) { if (!expandingCompressedTexture) { // Need to use glCompressedTexImage2D directly to allocate and fill this image // Avoid spurious memory allocation when possible gl.glCompressedTexImage2D( texTarget, 0, data.getInternalFormat(), texWidth, texHeight, data.getBorder(), data.getBuffer().capacity(), data.getBuffer()); } else { ByteBuffer buf = DDSImage.allocateBlankBuffer(texWidth, texHeight, data.getInternalFormat()); gl.glCompressedTexImage2D( texTarget, 0, data.getInternalFormat(), texWidth, texHeight, data.getBorder(), buf.capacity(), buf); updateSubImageImpl(data, texTarget, 0, 0, 0, 0, 0, data.getWidth(), data.getHeight()); } } else { if (data.getMipmap() && haveAutoMipmapGeneration) { // For now, only use hardware mipmapping for uncompressed 2D // textures where the user hasn't explicitly specified // mipmap data; don't know about interactions between // GL_GENERATE_MIPMAP and glCompressedTexImage2D gl.glTexParameteri(texParamTarget, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE); usingAutoMipmapGeneration = true; } gl.glTexImage2D( texTarget, 0, data.getInternalFormat(), texWidth, texHeight, data.getBorder(), data.getPixelFormat(), data.getPixelType(), null); updateSubImageImpl(data, texTarget, 0, 0, 0, 0, 0, data.getWidth(), data.getHeight()); } } } int minFilter = (data.getMipmap() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR); int magFilter = GL.GL_LINEAR; int wrapMode = (gl.isExtensionAvailable("GL_VERSION_1_2") ? GL.GL_CLAMP_TO_EDGE : GL.GL_CLAMP); // REMIND: figure out what to do for GL_TEXTURE_RECTANGLE_ARB if (texTarget != GL.GL_TEXTURE_RECTANGLE_ARB) { gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter); gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter); gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_S, wrapMode); gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_T, wrapMode); if (this.target == GL.GL_TEXTURE_CUBE_MAP) { gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_R, wrapMode); } } // Don't overwrite target if we're loading e.g. faces of a cube // map if ((this.target == 0) || (this.target == GL.GL_TEXTURE_2D) || (this.target == GL.GL_TEXTURE_RECTANGLE_ARB)) { this.target = texTarget; } // This estimate will be wrong for cube maps estimatedMemorySize = data.getEstimatedMemorySize(); } /** * Updates a subregion of the content area of this texture using the given data. If automatic * mipmap generation is in use (see {@link #isUsingAutoMipmapGeneration * isUsingAutoMipmapGeneration}), updates to the base (level 0) mipmap will cause the lower-level * mipmaps to be regenerated, and updates to other mipmap levels will be ignored. Otherwise, if * automatic mipmap generation is not in use, only updates the specified mipmap level and does not * re-generate mipmaps if they were originally produced or loaded. * * @param data the image data to be uploaded to this texture * @param mipmapLevel the mipmap level of the texture to set. If this is non-zero and the * TextureData contains mipmap data, the appropriate mipmap level will be selected. * @param x the x offset (in pixels) relative to the lower-left corner of this texture * @param y the y offset (in pixels) relative to the lower-left corner of this texture * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void updateSubImage(TextureData data, int mipmapLevel, int x, int y) throws GLException { if (usingAutoMipmapGeneration && mipmapLevel != 0) { // When we're using mipmap generation via GL_GENERATE_MIPMAP, we // don't need to update other mipmap levels return; } bind(); updateSubImageImpl(data, target, mipmapLevel, x, y, 0, 0, data.getWidth(), data.getHeight()); } /** * Updates a subregion of the content area of this texture using the specified sub-region of the * given data. If automatic mipmap generation is in use (see {@link #isUsingAutoMipmapGeneration * isUsingAutoMipmapGeneration}), updates to the base (level 0) mipmap will cause the lower-level * mipmaps to be regenerated, and updates to other mipmap levels will be ignored. Otherwise, if * automatic mipmap generation is not in use, only updates the specified mipmap level and does not * re-generate mipmaps if they were originally produced or loaded. This method is only supported * for uncompressed TextureData sources. * * @param data the image data to be uploaded to this texture * @param mipmapLevel the mipmap level of the texture to set. If this is non-zero and the * TextureData contains mipmap data, the appropriate mipmap level will be selected. * @param dstx the x offset (in pixels) relative to the lower-left corner of this texture where * the update will be applied * @param dsty the y offset (in pixels) relative to the lower-left corner of this texture where * the update will be applied * @param srcx the x offset (in pixels) relative to the lower-left corner of the supplied * TextureData from which to fetch the update rectangle * @param srcy the y offset (in pixels) relative to the lower-left corner of the supplied * TextureData from which to fetch the update rectangle * @param width the width (in pixels) of the rectangle to be updated * @param height the height (in pixels) of the rectangle to be updated * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void updateSubImage( TextureData data, int mipmapLevel, int dstx, int dsty, int srcx, int srcy, int width, int height) throws GLException { if (data.isDataCompressed()) { throw new GLException( "updateSubImage specifying a sub-rectangle is not supported for compressed TextureData"); } if (usingAutoMipmapGeneration && mipmapLevel != 0) { // When we're using mipmap generation via GL_GENERATE_MIPMAP, we // don't need to update other mipmap levels return; } bind(); updateSubImageImpl(data, target, mipmapLevel, dstx, dsty, srcx, srcy, width, height); } /** * Sets the OpenGL floating-point texture parameter for the texture's target. This gives control * over parameters such as GL_TEXTURE_MAX_ANISOTROPY_EXT. Causes this texture to be bound to the * current texture state. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void setTexParameterf(int parameterName, float value) { bind(); GL gl = GLU.getCurrentGL(); gl.glTexParameterf(target, parameterName, value); } /** * Sets the OpenGL multi-floating-point texture parameter for the texture's target. Causes this * texture to be bound to the current texture state. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void setTexParameterfv(int parameterName, FloatBuffer params) { bind(); GL gl = GLU.getCurrentGL(); gl.glTexParameterfv(target, parameterName, params); } /** * Sets the OpenGL multi-floating-point texture parameter for the texture's target. Causes this * texture to be bound to the current texture state. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void setTexParameterfv(int parameterName, float[] params, int params_offset) { bind(); GL gl = GLU.getCurrentGL(); gl.glTexParameterfv(target, parameterName, params, params_offset); } /** * Sets the OpenGL integer texture parameter for the texture's target. This gives control over * parameters such as GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, which by default are set to * GL_CLAMP_TO_EDGE if OpenGL 1.2 is supported on the current platform and GL_CLAMP if not. Causes * this texture to be bound to the current texture state. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void setTexParameteri(int parameterName, int value) { bind(); GL gl = GLU.getCurrentGL(); gl.glTexParameteri(target, parameterName, value); } /** * Sets the OpenGL multi-integer texture parameter for the texture's target. Causes this texture * to be bound to the current texture state. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void setTexParameteriv(int parameterName, IntBuffer params) { bind(); GL gl = GLU.getCurrentGL(); gl.glTexParameteriv(target, parameterName, params); } /** * Sets the OpenGL multi-integer texture parameter for the texture's target. Causes this texture * to be bound to the current texture state. * * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred */ public void setTexParameteriv(int parameterName, int[] params, int params_offset) { bind(); GL gl = GLU.getCurrentGL(); gl.glTexParameteriv(target, parameterName, params, params_offset); } /** * Returns the underlying OpenGL texture object for this texture. Most applications will not need * to access this, since it is handled automatically by the bind() and dispose() APIs. */ public int getTextureObject() { return texID; } /** * Returns an estimate of the amount of texture memory in bytes this Texture consumes. It should * only be treated as an estimate; most applications should not need to query this but instead let * the OpenGL implementation page textures in and out as necessary. */ public int getEstimatedMemorySize() { return estimatedMemorySize; } /** * Indicates whether this Texture is using automatic mipmap generation (via the OpenGL texture * parameter GL_GENERATE_MIPMAP). This will automatically be used when mipmapping is requested via * the TextureData and either OpenGL 1.4 or the GL_SGIS_generate_mipmap extension is available. If * so, updates to the base image (mipmap level 0) will automatically propagate down to the lower * mipmap levels. Manual updates of the mipmap data at these lower levels will be ignored. */ public boolean isUsingAutoMipmapGeneration() { return usingAutoMipmapGeneration; } // ---------------------------------------------------------------------- // Internals only below this point // /** * Returns true if the given value is a power of two. * * @return true if the given value is a power of two, false otherwise */ private static boolean isPowerOfTwo(int val) { return ((val & (val - 1)) == 0); } /** * Returns the nearest power of two that is larger than the given value. If the given value is * already a power of two, this method will simply return that value. * * @param val the value * @return the next power of two */ private static int nextPowerOfTwo(int val) { int ret = 1; while (ret < val) { ret <<= 1; } return ret; } /** Updates the actual image dimensions; usually only called from <code>updateImage</code>. */ private void setImageSize(int width, int height, int target) { imgWidth = width; imgHeight = height; if (target == GL.GL_TEXTURE_RECTANGLE_ARB) { if (mustFlipVertically) { coords = new TextureCoords(0, imgHeight, imgWidth, 0); } else { coords = new TextureCoords(0, 0, imgWidth, imgHeight); } } else { if (mustFlipVertically) { coords = new TextureCoords( 0, (float) imgHeight / (float) texHeight, (float) imgWidth / (float) texWidth, 0); } else { coords = new TextureCoords( 0, 0, (float) imgWidth / (float) texWidth, (float) imgHeight / (float) texHeight); } } } private void updateSubImageImpl( TextureData data, int newTarget, int mipmapLevel, int dstx, int dsty, int srcx, int srcy, int width, int height) throws GLException { GL gl = GLU.getCurrentGL(); data.setHaveEXTABGR(gl.isExtensionAvailable("GL_EXT_abgr")); data.setHaveGL12(gl.isExtensionAvailable("GL_VERSION_1_2")); Buffer buffer = data.getBuffer(); if (buffer == null && data.getMipmapData() == null) { // Assume user just wanted to get the Texture object allocated return; } int rowlen = data.getRowLength(); int dataWidth = data.getWidth(); int dataHeight = data.getHeight(); if (data.getMipmapData() != null) { // Compute the width, height and row length at the specified mipmap level // Note we do not support specification of the row length for // mipmapped textures at this point for (int i = 0; i < mipmapLevel; i++) { width = Math.max(width / 2, 1); height = Math.max(height / 2, 1); dataWidth = Math.max(dataWidth / 2, 1); dataHeight = Math.max(dataHeight / 2, 1); } rowlen = 0; buffer = data.getMipmapData()[mipmapLevel]; } // Clip incoming rectangles to what is available both on this // texture and in the incoming TextureData if (srcx < 0) { width += srcx; srcx = 0; } if (srcy < 0) { height += srcy; srcy = 0; } // NOTE: not sure whether the following two are the correct thing to do if (dstx < 0) { width += dstx; dstx = 0; } if (dsty < 0) { height += dsty; dsty = 0; } if (srcx + width > dataWidth) { width = dataWidth - srcx; } if (srcy + height > dataHeight) { height = dataHeight - srcy; } if (dstx + width > texWidth) { width = texWidth - dstx; } if (dsty + height > texHeight) { height = texHeight - dsty; } checkCompressedTextureExtensions(data); if (data.isDataCompressed()) { gl.glCompressedTexSubImage2D( newTarget, mipmapLevel, dstx, dsty, width, height, data.getInternalFormat(), buffer.remaining(), buffer); } else { int[] align = new int[1]; int[] rowLength = new int[1]; int[] skipRows = new int[1]; int[] skipPixels = new int[1]; gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment gl.glGetIntegerv(GL.GL_UNPACK_ROW_LENGTH, rowLength, 0); // save row length gl.glGetIntegerv(GL.GL_UNPACK_SKIP_ROWS, skipRows, 0); // save skipped rows gl.glGetIntegerv(GL.GL_UNPACK_SKIP_PIXELS, skipPixels, 0); // save skipped pixels gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment()); if (DEBUG && VERBOSE) { System.out.println("Row length = " + rowlen); System.out.println("skip pixels = " + srcx); System.out.println("skip rows = " + srcy); System.out.println("dstx = " + dstx); System.out.println("dsty = " + dsty); System.out.println("width = " + width); System.out.println("height = " + height); } gl.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, rowlen); gl.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, srcy); gl.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, srcx); gl.glTexSubImage2D( newTarget, mipmapLevel, dstx, dsty, width, height, data.getPixelFormat(), data.getPixelType(), buffer); gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment gl.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, rowLength[0]); // restore row length gl.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, skipRows[0]); // restore skipped rows gl.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, skipPixels[0]); // restore skipped pixels } } private void checkCompressedTextureExtensions(TextureData data) { GL gl = GLU.getCurrentGL(); if (data.isDataCompressed()) { switch (data.getInternalFormat()) { case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: if (!gl.isExtensionAvailable("GL_EXT_texture_compression_s3tc") && !gl.isExtensionAvailable("GL_NV_texture_compression_vtc")) { throw new GLException("DXTn compressed textures not supported by this graphics card"); } break; default: // FIXME: should test availability of more texture // compression extensions here break; } } } /** * Creates a new texture ID. * * @param gl the GL object associated with the current OpenGL context * @return a new texture ID */ private static int createTextureID(GL gl) { int[] tmp = new int[1]; gl.glGenTextures(1, tmp, 0); return tmp[0]; } // Helper routines for disabling certain codepaths private static boolean haveNPOT(GL gl) { return (!disableNPOT && gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two")); } private static boolean haveTexRect(GL gl) { return (!disableTexRect && TextureIO.isTexRectEnabled() && gl.isExtensionAvailable("GL_ARB_texture_rectangle")); } }
public class WindowsOnscreenGLDrawable extends WindowsGLDrawable { public static final int LOCK_SURFACE_NOT_READY = 1; public static final int LOCK_SURFACE_CHANGED = 2; public static final int LOCK_SUCCESS = 3; protected Component component; // Variables for lockSurface/unlockSurface private JAWT_DrawingSurface ds; private JAWT_DrawingSurfaceInfo dsi; private JAWT_Win32DrawingSurfaceInfo win32dsi; // Indicates whether the component (if an onscreen context) has been // realized. Plausibly, before the component is realized the JAWT // should return an error or NULL object from some of its // operations; this appears to be the case on Win32 but is not true // at least with Sun's current X11 implementation (1.4.x), which // crashes with no other error reported if the DrawingSurfaceInfo is // fetched from a locked DrawingSurface during the validation as a // result of calling show() on the main thread. To work around this // we prevent any JAWT or OpenGL operations from being done until // addNotify() is called on the component. protected boolean realized; private static final boolean PROFILING = Debug.debug("WindowsOnscreenGLDrawable.profiling"); private static final int PROFILING_TICKS = 200; private int profilingLockSurfaceTicks; private long profilingLockSurfaceTime; private int profilingUnlockSurfaceTicks; private long profilingUnlockSurfaceTime; private int profilingSwapBuffersTicks; private long profilingSwapBuffersTime; // Workaround for problems on Intel 82855 cards private int setPixelFormatFailCount; private static final int MAX_SET_PIXEL_FORMAT_FAIL_COUNT = 5; public WindowsOnscreenGLDrawable( Component component, GLCapabilities capabilities, GLCapabilitiesChooser chooser) { super(capabilities, chooser); this.component = component; } public GLContext createContext(GLContext shareWith) { return new WindowsOnscreenGLContext(this, shareWith); } public void setRealized(boolean realized) { this.realized = realized; if (!realized) { // Assume heavyweight widget was destroyed setChosenGLCapabilities(null); pixelFormatChosen = false; } } public void setSize(int width, int height) { component.setSize(width, height); } public int getWidth() { return component.getWidth(); } public int getHeight() { return component.getHeight(); } public void swapBuffers() throws GLException { boolean didLock = false; if (hdc == 0) { if (lockSurface() == LOCK_SURFACE_NOT_READY) { return; } didLock = true; } long startTime = 0; if (PROFILING) { startTime = System.currentTimeMillis(); } if (!WGL.SwapBuffers(hdc) && (WGL.GetLastError() != 0)) { throw new GLException("Error swapping buffers"); } if (PROFILING) { long endTime = System.currentTimeMillis(); profilingSwapBuffersTime += (endTime - startTime); int ticks = PROFILING_TICKS; if (++profilingSwapBuffersTicks == ticks) { System.err.println( "SwapBuffers calls: " + profilingSwapBuffersTime + " ms / " + ticks + " calls (" + ((float) profilingSwapBuffersTime / (float) ticks) + " ms/call)"); profilingSwapBuffersTime = 0; profilingSwapBuffersTicks = 0; } } if (didLock) { unlockSurface(); } } public int lockSurface() throws GLException { if (!realized) { return LOCK_SURFACE_NOT_READY; } if (hdc != 0) { throw new GLException("Surface already locked"); } long startTime = 0; if (PROFILING) { startTime = System.currentTimeMillis(); } ds = JAWT.getJAWT().GetDrawingSurface(component); if (ds == null) { // Widget not yet realized return LOCK_SURFACE_NOT_READY; } int res = ds.Lock(); if ((res & JAWTFactory.JAWT_LOCK_ERROR) != 0) { throw new GLException("Unable to lock surface"); } // See whether the surface changed and if so destroy the old // OpenGL context so it will be recreated (NOTE: removeNotify // should handle this case, but it may be possible that race // conditions can cause this code to be triggered -- should test // more) int ret = LOCK_SUCCESS; if ((res & JAWTFactory.JAWT_LOCK_SURFACE_CHANGED) != 0) { ret = LOCK_SURFACE_CHANGED; } dsi = ds.GetDrawingSurfaceInfo(); if (dsi == null) { // Widget not yet realized ds.Unlock(); JAWT.getJAWT().FreeDrawingSurface(ds); ds = null; return LOCK_SURFACE_NOT_READY; } win32dsi = (JAWT_Win32DrawingSurfaceInfo) dsi.platformInfo(); hdc = win32dsi.hdc(); if (hdc == 0) { // Widget not yet realized ds.FreeDrawingSurfaceInfo(dsi); ds.Unlock(); JAWT.getJAWT().FreeDrawingSurface(ds); ds = null; dsi = null; win32dsi = null; return LOCK_SURFACE_NOT_READY; } if (!pixelFormatChosen) { try { choosePixelFormat(true); setPixelFormatFailCount = 0; } catch (RuntimeException e) { // Workaround for problems seen on Intel 82855 cards in particular // Make it look like the lockSurface() call didn't succeed unlockSurface(); if (e instanceof GLException) { if (++setPixelFormatFailCount == MAX_SET_PIXEL_FORMAT_FAIL_COUNT) { setPixelFormatFailCount = 0; throw e; } return LOCK_SURFACE_NOT_READY; } else { // Probably a user error in the GLCapabilitiesChooser or similar. // Don't propagate non-GLExceptions out because calling code // expects to catch only that exception type throw new GLException(e); } } } if (PROFILING) { long endTime = System.currentTimeMillis(); profilingLockSurfaceTime += (endTime - startTime); int ticks = PROFILING_TICKS; if (++profilingLockSurfaceTicks == ticks) { System.err.println( "LockSurface calls: " + profilingLockSurfaceTime + " ms / " + ticks + " calls (" + ((float) profilingLockSurfaceTime / (float) ticks) + " ms/call)"); profilingLockSurfaceTime = 0; profilingLockSurfaceTicks = 0; } } return ret; } public void unlockSurface() { if (hdc == 0) { throw new GLException("Surface already unlocked"); } long startTime = 0; if (PROFILING) { startTime = System.currentTimeMillis(); } ds.FreeDrawingSurfaceInfo(dsi); ds.Unlock(); JAWT.getJAWT().FreeDrawingSurface(ds); ds = null; dsi = null; win32dsi = null; hdc = 0; if (PROFILING) { long endTime = System.currentTimeMillis(); profilingUnlockSurfaceTime += (endTime - startTime); int ticks = PROFILING_TICKS; if (++profilingUnlockSurfaceTicks == ticks) { System.err.println( "UnlockSurface calls: " + profilingUnlockSurfaceTime + " ms / " + ticks + " calls (" + ((float) profilingUnlockSurfaceTime / (float) ticks) + " ms/call)"); profilingUnlockSurfaceTime = 0; profilingUnlockSurfaceTicks = 0; } } } }
public abstract class GLDrawableImpl implements GLDrawable { protected static final boolean DEBUG = Debug.debug("GLDrawable"); protected GLDrawableImpl(GLDrawableFactory factory, NativeSurface comp, boolean realized) { this.factory = factory; this.surface = comp; this.realized = realized; this.requestedCapabilities = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getRequestedCapabilities(); } /** Returns the DynamicLookupHelper */ public abstract GLDynamicLookupHelper getGLDynamicLookupHelper(); public final GLDrawableFactoryImpl getFactoryImpl() { return (GLDrawableFactoryImpl) getFactory(); } @Override public final void swapBuffers() throws GLException { if (!realized) { return; // destroyed already } int lockRes = lockSurface(); // it's recursive, so it's ok within [makeCurrent .. release] if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { return; } try { if (NativeSurface.LOCK_SURFACE_CHANGED == lockRes) { updateHandle(); } final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); if (caps.getDoubleBuffered()) { if (!surface.surfaceSwap()) { swapBuffersImpl(true); } } else { final GLContext ctx = GLContext.getCurrent(); if (null != ctx && ctx.getGLDrawable() == this) { ctx.getGL().glFlush(); } swapBuffersImpl(false); } } finally { unlockSurface(); } surface.surfaceUpdated(this, surface, System.currentTimeMillis()); } /** * Platform and implementation depending surface swap. * * <p>The surface is locked. * * <p>If <code>doubleBuffered</code> is <code>true</code>, an actual platform dependent surface * swap shall be executed. * * <p>If <code>doubleBuffered</code> is <code>false</code>, {@link GL#glFlush()} has been called * already and the implementation may execute implementation specific code. */ protected abstract void swapBuffersImpl(boolean doubleBuffered); public static final String toHexString(long hex) { return "0x" + Long.toHexString(hex); } @Override public final GLProfile getGLProfile() { return requestedCapabilities.getGLProfile(); } @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { return (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); } public final GLCapabilitiesImmutable getRequestedGLCapabilities() { return requestedCapabilities; } @Override public NativeSurface getNativeSurface() { return surface; } /** called with locked surface @ setRealized(false) */ protected void destroyHandle() {} /** called with locked surface @ setRealized(true) or @ lockSurface(..) when surface changed */ protected void updateHandle() {} @Override public long getHandle() { return surface.getSurfaceHandle(); } @Override public final GLDrawableFactory getFactory() { return factory; } @Override public final synchronized void setRealized(boolean realizedArg) { if (realized != realizedArg) { if (DEBUG) { System.err.println( getThreadName() + ": setRealized: " + getClass().getSimpleName() + " " + realized + " -> " + realizedArg); } realized = realizedArg; AbstractGraphicsDevice aDevice = surface.getGraphicsConfiguration().getScreen().getDevice(); if (realizedArg) { if (surface instanceof ProxySurface) { ((ProxySurface) surface).createNotify(); } if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockSurface()) { throw new GLException( "GLDrawableImpl.setRealized(true): Surface not ready (lockSurface)"); } } else { aDevice.lock(); } try { if (realizedArg) { setRealizedImpl(); updateHandle(); } else { destroyHandle(); setRealizedImpl(); } } finally { if (realizedArg) { unlockSurface(); } else { aDevice.unlock(); if (surface instanceof ProxySurface) { ((ProxySurface) surface).destroyNotify(); } } } } else if (DEBUG) { System.err.println( getThreadName() + ": setRealized: " + getClass().getName() + " " + this.realized + " == " + realizedArg); } } /** Platform specific realization of drawable */ protected abstract void setRealizedImpl(); /** * Callback for special implementations, allowing GLContext to trigger GL related lifecycle: * <code>construct</code>, <code>destroy</code>. * * <p>If <code>realized</code> is <code>true</code>, the context has just been created and made * current. * * <p>If <code>realized</code> is <code>false</code>, the context is still current and will be * released and destroyed after this method returns. * * <p> * * @see #contextMadeCurrent(GLContext, boolean) */ protected void contextRealized(GLContext glc, boolean realized) {} /** * Callback for special implementations, allowing GLContext to trigger GL related lifecycle: * <code>makeCurrent</code>, <code>release</code>. * * <p>If <code>current</code> is <code>true</code>, the context has just been made current. * * <p>If <code>current</code> is <code>false</code>, the context is still current and will be * release after this method returns. * * <p>Note: Will also be called after {@link #contextRealized(GLContext, boolean) * contextRealized(ctx, true)} but not at context destruction, i.e. {@link * #contextRealized(GLContext, boolean) contextRealized(ctx, false)}. * * @see #contextRealized(GLContext, boolean) */ protected void contextMadeCurrent(GLContext glc, boolean current) {} /** * Callback for special implementations, allowing to associate bound context to this drawable * (bound == true) or to remove such association (bound == false). * * @param ctx the just bounded or unbounded context * @param bound if <code>true</code> create an association, otherwise remove it */ protected void associateContext(GLContext ctx, boolean bound) {} /** * Callback for special implementations, allowing GLContext to fetch a custom default render * framebuffer. Defaults to zero. */ protected int getDefaultDrawFramebuffer() { return 0; } /** * Callback for special implementations, allowing GLContext to fetch a custom default read * framebuffer. Defaults to zero. */ protected int getDefaultReadFramebuffer() { return 0; } @Override public final synchronized boolean isRealized() { return realized; } @Override public int getWidth() { return surface.getWidth(); } @Override public int getHeight() { return surface.getHeight(); } /** @see NativeSurface#lockSurface() */ public final int lockSurface() throws GLException { return surface.lockSurface(); } /** @see NativeSurface#unlockSurface() */ public final void unlockSurface() { surface.unlockSurface(); } @Override public String toString() { return getClass().getSimpleName() + "[Realized " + isRealized() + ",\n\tFactory " + getFactory() + ",\n\tHandle " + toHexString(getHandle()) + ",\n\tSurface " + getNativeSurface() + "]"; } protected static String getThreadName() { return Thread.currentThread().getName(); } protected GLDrawableFactory factory; protected NativeSurface surface; protected GLCapabilitiesImmutable requestedCapabilities; // Indicates whether the surface (if an onscreen context) has been // realized. Plausibly, before the surface is realized the JAWT // should return an error or NULL object from some of its // operations; this appears to be the case on Win32 but is not true // at least with Sun's current X11 implementation (1.4.x), which // crashes with no other error reported if the DrawingSurfaceInfo is // fetched from a locked DrawingSurface during the validation as a // result of calling show() on the main thread. To work around this // we prevent any JAWT or OpenGL operations from being done until // addNotify() is called on the surface. protected boolean realized; }
/** * Tracks as closely as possible the sizes of allocated OpenGL buffer objects. When glMapBuffer or * glMapBufferARB is called, in order to turn the resulting base address into a java.nio.ByteBuffer, * we need to know the size in bytes of the allocated OpenGL buffer object. Previously we would * compute this size by using glGetBufferParameterivARB with a pname of GL_BUFFER_SIZE, but it * appears doing so each time glMapBuffer is called is too costly on at least Apple's new * multithreaded OpenGL implementation. * * <p>Instead we now try to track the sizes of allocated buffer objects. We watch calls to * glBindBuffer to see which buffer is bound to which target and to glBufferData to see how large * the buffer's allocated size is. When glMapBuffer is called, we consult our table of buffer sizes * to see if we can return an answer without a glGet call. * * <p>We share the GLBufferSizeTracker objects among all GLContexts for which sharing is enabled, * because the namespace for buffer objects is the same for these contexts. * * <p>Tracking the state of which buffer objects are bound is done in the GLBufferStateTracker and * is not completely trivial. In the face of calls to glPushClientAttrib / glPopClientAttrib we * currently punt and re-fetch the bound buffer object for the state in question; see, for example, * glVertexPointer and the calls down to GLBufferStateTracker.getBoundBufferObject(). Note that we * currently ignore new binding targets such as GL_TRANSFORM_FEEDBACK_BUFFER_NV; the fact that new * binding targets may be added in the future makes it impossible to cache state for these new * targets. * * <p>Ignoring new binding targets, the primary situation in which we may not be able to return a * cached answer is in the case of an error, where glBindBuffer may not have been called before * trying to call glBufferData. Also, if external native code modifies a buffer object, we may * return an incorrect answer. (FIXME: this case requires more thought, and perhaps stochastic and * exponential-fallback checking. However, note that it can only occur in the face of external * native code which requires that the application be signed anyway, so there is no security risk in * this area.) */ public class GLBufferSizeTracker { // Map from buffer names to sizes. // Note: should probably have some way of shrinking this map, but // can't just make it a WeakHashMap because nobody holds on to the // keys; would have to always track creation and deletion of buffer // objects, which is probably sub-optimal. The expected usage // pattern of buffer objects indicates that the fact that this map // never shrinks is probably not that bad. private IntLongHashMap bufferSizeMap; protected static final boolean DEBUG = Debug.debug("GLStatusTracker"); public GLBufferSizeTracker() { bufferSizeMap = new IntLongHashMap(); bufferSizeMap.setKeyNotFoundValue(-1); } public void setBufferSize( GLBufferStateTracker bufferStateTracker, int target, GL caller, long size) { // Need to do some similar queries to getBufferSize below int buffer = bufferStateTracker.getBoundBufferObject(target, caller); if (buffer != 0) { setDirectStateBufferSize(buffer, caller, size); } // We don't know the current buffer state. Note that the buffer // state tracker will have made the appropriate OpenGL query if it // didn't know what was going on, so at this point we have nothing // left to do except drop this piece of information on the floor. } public void setDirectStateBufferSize(int buffer, GL caller, long size) { bufferSizeMap.put(buffer, size); } public long getBufferSize(GLBufferStateTracker bufferStateTracker, int target, GL caller) { // See whether we know what buffer is currently bound to the given // state final int buffer = bufferStateTracker.getBoundBufferObject(target, caller); if (0 != buffer) { return getBufferSizeImpl(target, buffer, caller); } // We don't know what's going on in this case; query the GL for an answer // FIXME: both functions return 'int' types, which is not suitable, // since buffer lenght is 64bit ? int[] tmp = new int[1]; caller.glGetBufferParameteriv(target, GL.GL_BUFFER_SIZE, tmp, 0); if (DEBUG) { System.err.println("GLBufferSizeTracker.getBufferSize(): no cached buffer information"); } return (long) tmp[0]; } public long getDirectStateBufferSize(int buffer, GL caller) { return getBufferSizeImpl(0, buffer, caller); } private long getBufferSizeImpl(int target, int buffer, GL caller) { // See whether we know the size of this buffer object; at this // point we almost certainly should if the application is // written correctly long sz = bufferSizeMap.get(buffer); if (0 > sz) { // For robustness, try to query this value from the GL as we used to // FIXME: both functions return 'int' types, which is not suitable, // since buffer lenght is 64bit ? int[] tmp = new int[1]; if (0 == target) { // DirectState .. if (caller.isFunctionAvailable("glGetNamedBufferParameterivEXT")) { caller.getGL2().glGetNamedBufferParameterivEXT(buffer, GL.GL_BUFFER_SIZE, tmp, 0); } else { throw new GLException( "Error: getDirectStateBufferSize called with unknown state and GL function 'glGetNamedBufferParameterivEXT' n/a to query size"); } } else { caller.glGetBufferParameteriv(target, GL.GL_BUFFER_SIZE, tmp, 0); } if (tmp[0] == 0) { // Assume something is wrong rather than silently going along throw new GLException( "Error: buffer size returned by " + ((0 == target) ? "glGetNamedBufferParameterivEXT" : "glGetBufferParameteriv") + " was zero; probably application error"); } // Assume we just don't know what's happening sz = (long) tmp[0]; bufferSizeMap.put(buffer, sz); if (DEBUG) { System.err.println( "GLBufferSizeTracker.getBufferSize(): made slow query to cache size " + sz + " for buffer " + buffer); } } return sz; } // This should be called on any major event where we might start // producing wrong answers, such as OpenGL context creation and // destruction if we don't know whether there are other currently- // created contexts that might be keeping the buffer objects alive // that we're dealing with public void clearCachedBufferSizes() { bufferSizeMap.clear(); } }
/** * Load addressbook and apply filter, returning messages about this. * To control memory, don't load the whole addressbook if we can help it... * only load what is searched for. */ @Override public String getLoadBookMessages() { if (isDirect()) return super.getLoadBookMessages(); NamingService service = getNamingService(); Debug.debug("Searching within " + service + " with filename=" + getFileName() + " and with filter=" + filter + " and with search=" + search); String message = ""; try { LinkedList<AddressBean> list = new LinkedList<AddressBean>(); Map<String, Destination> results; Properties searchProps = new Properties(); // only blockfile needs this searchProps.setProperty("list", getFileName()); if (filter != null) { String startsAt = filter.equals("0-9") ? "[0-9]" : filter; searchProps.setProperty("startsWith", startsAt); } if (isPrefiltered()) { // Only limit if we not searching or filtering, so we will // know the total number of results if (beginIndex > 0) searchProps.setProperty("skip", Integer.toString(beginIndex)); int limit = 1 + endIndex - beginIndex; if (limit > 0) searchProps.setProperty("limit", Integer.toString(limit)); } if (search != null && search.length() > 0) searchProps.setProperty("search", search.toLowerCase(Locale.US)); results = service.getEntries(searchProps); Debug.debug("Result count: " + results.size()); for (Map.Entry<String, Destination> entry : results.entrySet()) { String name = entry.getKey(); if( filter != null && filter.length() > 0 ) { if (filter.equals("0-9")) { char first = name.charAt(0); if( first < '0' || first > '9' ) continue; } else if( ! name.toLowerCase(Locale.US).startsWith( filter.toLowerCase(Locale.US) ) ) { continue; } } if( search != null && search.length() > 0 ) { if( name.indexOf( search ) == -1 ) { continue; } } String destination = entry.getValue().toBase64(); if (destination != null) { list.addLast( new AddressBean( name, destination ) ); } else { // delete it too? System.err.println("Bad entry " + name + " in database " + service.getName()); } } AddressBean array[] = list.toArray(new AddressBean[list.size()]); Arrays.sort( array, sorter ); entries = array; message = generateLoadMessage(); } catch (Exception e) { Debug.debug( e.getClass().getName() + ": " + e.getMessage() ); } if( message.length() > 0 ) message = "<p>" + message + "</p>"; return message; }