/* * Copyright (c) 2003 Sun Microsystems, Inc. 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 * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package net.java.games.jogl.impl; import java.awt.Component; import net.java.games.jogl.*; import net.java.games.gluegen.runtime.*; public abstract class GLContextImpl extends GLContext { protected GLContextLock lock = new GLContextLock(); protected static final boolean DEBUG = Debug.debug("GLContextImpl"); protected static final boolean VERBOSE = Debug.verbose(); protected static final boolean NO_FREE = Debug.isPropertyDefined("jogl.GLContext.nofree"); static { NativeLibLoader.load(); } protected GLCapabilities capabilities; protected GLCapabilitiesChooser chooser; protected Component component; // Cache of the functions that are available to be called at the current // moment in time protected FunctionAvailabilityCache functionAvailability; protected GL gl; protected GLU glu = new GLUImpl(gluProcAddressTable); protected static final GLUProcAddressTable gluProcAddressTable = new GLUProcAddressTable(); protected static boolean haveResetGLUProcAddressTable; public GLContextImpl(Component component, GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLContext shareWith) { this.component = component; this.capabilities = (GLCapabilities) capabilities.clone(); this.chooser = chooser; setGL(createGL()); functionAvailability = new FunctionAvailabilityCache(this); if (shareWith != null) { GLContextShareSet.registerSharing(this, shareWith); } } public int makeCurrent() throws GLException { lock.lock(); int res = 0; try { res = makeCurrentImpl(); } catch (GLException e) { lock.unlock(); throw(e); } if (res == CONTEXT_NOT_CURRENT) { lock.unlock(); } else { setCurrent(this); } return res; } protected abstract int makeCurrentImpl() throws GLException; public void release() throws GLException { if (!lock.isHeld()) { throw new GLException("Context not current on current thread"); } setCurrent(null); try { releaseImpl(); } finally { lock.unlock(); } } protected abstract void releaseImpl() throws GLException; public void destroy() { // Should we check the lock state? It should not be current on any // thread. destroyImpl(); } protected abstract void destroyImpl() throws GLException; public boolean isSynchronized() { return !lock.getFailFastMode(); } public void setSynchronized(boolean isSynchronized) { lock.setFailFastMode(!isSynchronized); } public GL getGL() { return gl; } public void setGL(GL gl) { this.gl = gl; // Also reset the GL object for the pure-Java GLU implementation ((GLUImpl) glu).setGL(gl); } public GLU getGLU() { return glu; } public void setGLU(GLU glu) { this.glu = glu; } // Subclasses for onscreen GLContexts should override this to // receive a notification from the GLCanvas or other implementation // upon addNotify public void setRealized() { } //---------------------------------------------------------------------- // Helpers for various context implementations // // Flag for enabling / disabling automatic swapping of the front and // back buffers protected boolean autoSwapBuffers = true; // Offscreen context handling. Offscreen contexts should handle // these resize requests in makeCurrent and clear the // pendingOffscreenResize flag. protected boolean pendingOffscreenResize; protected int pendingOffscreenWidth; protected int pendingOffscreenHeight; /** Create the GL for this context. */ protected abstract GL createGL(); /** * Pbuffer support; indicates whether this context is capable of * creating a subordinate pbuffer context (distinct from an * "offscreen context", which is typically software-rendered on all * platforms). */ public abstract boolean canCreatePbufferContext(); /** * Pbuffer support; creates a subordinate GLContext for a pbuffer * associated with this context. */ public abstract GLContext createPbufferContext(GLCapabilities capabilities, int initialWidth, int initialHeight); /** * Pbuffer support; given that this is a GLContext associated with a * pbuffer, binds this pbuffer to its texture target. */ public abstract void bindPbufferToTexture(); /** * Pbuffer support; given that this is a GLContext associated with a * pbuffer, releases this pbuffer from its texture target. */ public abstract void releasePbufferFromTexture(); /* * Sets the swap interval for onscreen OpenGL contexts. Has no * effect for offscreen contexts. */ public void setSwapInterval(final int interval) { } /** Maps the given "platform-independent" function name to a real function name. Currently this is only used to map "glAllocateMemoryNV" and associated routines to wglAllocateMemoryNV / glXAllocateMemoryNV. */ protected abstract String mapToRealGLFunctionName(String glFunctionName); /** Maps the given "platform-independent" extension name to a real function name. Currently this is only used to map "GL_ARB_pbuffer" and "GL_ARB_pixel_format" to "WGL_ARB_pbuffer" and "WGL_ARB_pixel_format" (not yet mapped to X11). */ protected abstract String mapToRealGLExtensionName(String glExtensionName); public void setAutoSwapBufferMode(boolean autoSwapBuffers) { this.autoSwapBuffers = autoSwapBuffers; } public boolean getAutoSwapBufferMode() { return autoSwapBuffers; } /** Swaps the buffers of the OpenGL context if necessary. All error conditions cause a GLException to be thrown. */ public abstract void swapBuffers() throws GLException; /** Returns a non-null (but possibly empty) string containing the space-separated list of available platform-dependent (e.g., WGL, GLX) extensions. Can only be called while this context is current. */ public abstract String getPlatformExtensionsString(); /** Helper routine which resets a ProcAddressTable generated by the GLEmitter by looking up anew all of its function pointers. */ protected void resetProcAddressTable(Object table) { Class tableClass = table.getClass(); java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); for (int i = 0; i < fields.length; ++i) { String addressFieldName = fields[i].getName(); if (!addressFieldName.startsWith(ProcAddressHelper.PROCADDRESS_VAR_PREFIX)) { // not a proc address variable continue; } int startOfMethodName = ProcAddressHelper.PROCADDRESS_VAR_PREFIX.length(); String glFuncName = addressFieldName.substring(startOfMethodName); try { java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); assert(addressField.getType() == Long.TYPE); long newProcAddress = dynamicLookupFunction(glFuncName); // set the current value of the proc address variable in the table object addressField.setLong(table, newProcAddress); if (DEBUG) { // System.err.println(glFuncName + " = 0x" + Long.toHexString(newProcAddress)); } } catch (Exception e) { throw new GLException("Cannot get GL proc address for method \"" + glFuncName + "\": Couldn't set value of field \"" + addressFieldName + "\" in class " + tableClass.getName(), e); } } } /** Dynamically looks up the given function. */ protected abstract long dynamicLookupFunction(String glFuncName); /** Indicates whether the underlying OpenGL context has been created. This is used to manage sharing of display lists and textures between contexts. */ public abstract boolean isCreated(); /** * Resets the cache of which GL functions are available for calling through this * context. See {@link #isFunctionAvailable(String)} for more information on * the definition of "available". */ protected void resetGLFunctionAvailability() { // In order to be able to allow the user to uniformly install the // debug and trace pipelines in their GLEventListener.init() // method (for both GLCanvas and GLJPanel), we need to reset the // actual GL object in the GLDrawable as well setGL(createGL()); functionAvailability.flush(); if (!haveResetGLUProcAddressTable) { if (DEBUG) { System.err.println(getThreadName() + ": !!! Initializing GLU extension address table"); } resetProcAddressTable(gluProcAddressTable); haveResetGLUProcAddressTable = true; // Only need to do this once globally } } /** * Returns true if the specified OpenGL core- or extension-function can be * successfully called using this GL context given the current host (OpenGL * client) and display (OpenGL server) configuration. * * See {@link GL#isFunctionAvailable(String)} for more details. * * @param glFunctionName the name of the OpenGL function (e.g., use * "glPolygonOffsetEXT" to check if the {@link * net.java.games.jogl.GL#glPolygonOffsetEXT(float,float)} is available). */ protected boolean isFunctionAvailable(String glFunctionName) { return functionAvailability.isFunctionAvailable(mapToRealGLFunctionName(glFunctionName)); } /** * Returns true if the specified OpenGL extension can be * successfully called using this GL context given the current host (OpenGL * client) and display (OpenGL server) configuration. * * See {@link GL#isExtensionAvailable(String)} for more details. * * @param glExtensionName the name of the OpenGL extension (e.g., * "GL_VERTEX_PROGRAM_ARB"). */ public boolean isExtensionAvailable(String glExtensionName) { return functionAvailability.isExtensionAvailable(mapToRealGLExtensionName(glExtensionName)); } /** Indicates which floating-point pbuffer implementation is in use. Returns one of GLPbuffer.APPLE_FLOAT, GLPbuffer.ATI_FLOAT, or GLPbuffer.NV_FLOAT. */ public int getFloatingPointMode() throws GLException { throw new GLException("Not supported on non-pbuffer contexts"); } /** Hook indicating whether the concrete GLContext implementation is offscreen and therefore whether we need to process resize requests. */ protected abstract boolean isOffscreen(); /** On some platforms the mismatch between OpenGL's coordinate system (origin at bottom left) and the window system's coordinate system (origin at top left) necessitates a vertical flip of pixels read from offscreen contexts. */ public abstract boolean offscreenImageNeedsVerticalFlip(); /** Only called for offscreen contexts; needed by glReadPixels */ public abstract int getOffscreenContextPixelDataType(); /** Routine needed only for offscreen contexts in order to resize the underlying bitmap. Called by GLJPanel. */ public void resizeOffscreenContext(int newWidth, int newHeight) { if (!isOffscreen()) { throw new GLException("Should only call for offscreen OpenGL contexts"); } pendingOffscreenResize = true; pendingOffscreenWidth = newWidth; pendingOffscreenHeight = newHeight; } protected static String getThreadName() { return Thread.currentThread().getName(); } }