1 /* 2 * Copyright (c) 2002-2008 LWJGL Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'LWJGL' nor the names of 17 * its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.lwjgl.opengl; 33 34 import org.lwjgl.LWJGLException; 35 import org.lwjgl.LWJGLUtil; 36 import org.lwjgl.PointerBuffer; 37 import org.lwjgl.Sys; 38 import org.lwjgl.opencl.KHRGLSharing; 39 import org.lwjgl.opencl.APPLEGLSharing; 40 41 import java.nio.ByteBuffer; 42 import java.nio.IntBuffer; 43 44 import static org.lwjgl.opengl.GL11.*; 45 46 /** 47 * <p/> 48 * Context encapsulates an OpenGL context. 49 * <p/> 50 * <p/> 51 * This class is thread-safe. 52 * 53 * @author elias_naur <elias_naur@users.sourceforge.net> 54 * @version $Revision$ 55 * $Id$ 56 */ 57 final class ContextGL implements Context { 58 59 /** The platform specific implementation of context methods */ 60 private static final ContextImplementation implementation; 61 62 /** The current Context */ 63 private static final ThreadLocal<ContextGL> current_context_local = new ThreadLocal<ContextGL>(); 64 65 /** Handle to the native GL rendering context */ 66 private final ByteBuffer handle; 67 private final PeerInfo peer_info; 68 69 private final ContextAttribs contextAttribs; 70 private final boolean forwardCompatible; 71 72 /** Whether the context has been destroyed */ 73 private boolean destroyed; 74 75 private boolean destroy_requested; 76 77 /** The thread that has this context current, or null. */ 78 private Thread thread; 79 80 static { Sys.initialize()81 Sys.initialize(); 82 implementation = createImplementation(); 83 } 84 createImplementation()85 private static ContextImplementation createImplementation() { 86 switch ( LWJGLUtil.getPlatform() ) { 87 case LWJGLUtil.PLATFORM_LINUX: 88 return new LinuxContextImplementation(); 89 case LWJGLUtil.PLATFORM_WINDOWS: 90 return new WindowsContextImplementation(); 91 case LWJGLUtil.PLATFORM_MACOSX: 92 return new MacOSXContextImplementation(); 93 default: 94 throw new IllegalStateException("Unsupported platform"); 95 } 96 } 97 getPeerInfo()98 PeerInfo getPeerInfo() { 99 return peer_info; 100 } 101 getContextAttribs()102 ContextAttribs getContextAttribs() { 103 return contextAttribs; 104 } 105 getCurrentContext()106 static ContextGL getCurrentContext() { 107 return current_context_local.get(); 108 } 109 110 /** Create a context with the specified peer info and shared context */ ContextGL(PeerInfo peer_info, ContextAttribs attribs, ContextGL shared_context)111 ContextGL(PeerInfo peer_info, ContextAttribs attribs, ContextGL shared_context) throws LWJGLException { 112 ContextGL context_lock = shared_context != null ? shared_context : this; 113 // If shared_context is not null, synchronize on it to make sure it is not deleted 114 // while this context is created. Otherwise, simply synchronize on ourself to avoid NPE 115 synchronized ( context_lock ) { 116 if ( shared_context != null && shared_context.destroyed ) 117 throw new IllegalArgumentException("Shared context is destroyed"); 118 GLContext.loadOpenGLLibrary(); 119 try { 120 this.peer_info = peer_info; 121 this.contextAttribs = attribs; 122 123 IntBuffer attribList; 124 if ( attribs != null ) { 125 attribList = attribs.getAttribList(); 126 forwardCompatible = attribs.isForwardCompatible(); 127 } else { 128 attribList = null; 129 forwardCompatible = false; 130 } 131 132 this.handle = implementation.create(peer_info, attribList, shared_context != null ? shared_context.handle : null); 133 } catch (LWJGLException e) { 134 GLContext.unloadOpenGLLibrary(); 135 throw e; 136 } 137 } 138 } 139 140 /** Release the current context (if any). After this call, no context is current. */ releaseCurrent()141 public void releaseCurrent() throws LWJGLException { 142 ContextGL current_context = getCurrentContext(); 143 if ( current_context != null ) { 144 implementation.releaseCurrentContext(); 145 GLContext.useContext(null); 146 current_context_local.set(null); 147 synchronized ( current_context ) { 148 current_context.thread = null; 149 current_context.checkDestroy(); 150 } 151 } 152 } 153 154 /** 155 * Release the context from its drawable. This is necessary on some platforms, 156 * like Mac OS X, where binding the context to a drawable and binding the context 157 * for rendering are two distinct actions and where calling releaseDrawable 158 * on every releaseCurrentContext results in artifacts. 159 */ releaseDrawable()160 public synchronized void releaseDrawable() throws LWJGLException { 161 if ( destroyed ) 162 throw new IllegalStateException("Context is destroyed"); 163 implementation.releaseDrawable(getHandle()); 164 } 165 166 /** Update the context. Should be called whenever it's drawable is moved or resized */ update()167 public synchronized void update() { 168 if ( destroyed ) 169 throw new IllegalStateException("Context is destroyed"); 170 implementation.update(getHandle()); 171 } 172 173 /** Swap the buffers on the current context. Only valid for double-buffered contexts */ swapBuffers()174 public static void swapBuffers() throws LWJGLException { 175 implementation.swapBuffers(); 176 } 177 canAccess()178 private boolean canAccess() { 179 return thread == null || Thread.currentThread() == thread; 180 } 181 checkAccess()182 private void checkAccess() { 183 if ( !canAccess() ) 184 throw new IllegalStateException("From thread " + Thread.currentThread() + ": " + thread + " already has the context current"); 185 } 186 187 /** Make the context current */ makeCurrent()188 public synchronized void makeCurrent() throws LWJGLException { 189 checkAccess(); 190 if ( destroyed ) 191 throw new IllegalStateException("Context is destroyed"); 192 thread = Thread.currentThread(); 193 current_context_local.set(this); 194 implementation.makeCurrent(peer_info, handle); 195 GLContext.useContext(this, forwardCompatible); 196 } 197 getHandle()198 ByteBuffer getHandle() { 199 return handle; 200 } 201 202 /** Query whether the context is current */ isCurrent()203 public synchronized boolean isCurrent() throws LWJGLException { 204 if ( destroyed ) 205 throw new IllegalStateException("Context is destroyed"); 206 return implementation.isCurrent(handle); 207 } 208 checkDestroy()209 private void checkDestroy() { 210 if ( !destroyed && destroy_requested ) { 211 try { 212 releaseDrawable(); 213 implementation.destroy(peer_info, handle); 214 CallbackUtil.unregisterCallbacks(this); 215 destroyed = true; 216 thread = null; 217 GLContext.unloadOpenGLLibrary(); 218 } catch (LWJGLException e) { 219 LWJGLUtil.log("Exception occurred while destroying context: " + e); 220 } 221 } 222 } 223 224 /** 225 * Set the buffer swap interval. This call is a best-attempt at changing 226 * the monitor swap interval, which is the minimum periodicity of color buffer swaps, 227 * measured in video frame periods, and is not guaranteed to be successful. 228 * <p/> 229 * A video frame period is the time required to display a full frame of video data. 230 */ setSwapInterval(int value)231 public static void setSwapInterval(int value) { 232 implementation.setSwapInterval(value); 233 } 234 235 /** 236 * Destroy the context. This method behaves the same as destroy() with the extra 237 * requirement that the context must be either current to the current thread or not 238 * current at all. 239 */ forceDestroy()240 public synchronized void forceDestroy() throws LWJGLException { 241 checkAccess(); 242 destroy(); 243 } 244 245 /** 246 * Request destruction of the Context. If the context is current, no context will be current after this call. 247 * The context is destroyed when no thread has it current. 248 */ destroy()249 public synchronized void destroy() throws LWJGLException { 250 if ( destroyed ) 251 return; 252 destroy_requested = true; 253 boolean was_current = isCurrent(); 254 int error = GL_NO_ERROR; 255 if ( was_current ) { 256 try { 257 // May fail on GLContext.getCapabilities() 258 error = glGetError(); 259 } catch (Exception e) { 260 // ignore 261 } 262 releaseCurrent(); 263 } 264 checkDestroy(); 265 if ( was_current && error != GL_NO_ERROR ) 266 throw new OpenGLException(error); 267 } 268 setCLSharingProperties(final PointerBuffer properties)269 public synchronized void setCLSharingProperties(final PointerBuffer properties) throws LWJGLException { 270 final ByteBuffer peer_handle = peer_info.lockAndGetHandle(); 271 try { 272 switch ( LWJGLUtil.getPlatform() ) { 273 case LWJGLUtil.PLATFORM_WINDOWS: 274 final WindowsContextImplementation implWindows = (WindowsContextImplementation)implementation; 275 properties.put(KHRGLSharing.CL_GL_CONTEXT_KHR).put(implWindows.getHGLRC(handle)); 276 properties.put(KHRGLSharing.CL_WGL_HDC_KHR).put(implWindows.getHDC(peer_handle)); 277 break; 278 case LWJGLUtil.PLATFORM_LINUX: 279 final LinuxContextImplementation implLinux = (LinuxContextImplementation)implementation; 280 properties.put(KHRGLSharing.CL_GL_CONTEXT_KHR).put(implLinux.getGLXContext(handle)); 281 properties.put(KHRGLSharing.CL_GLX_DISPLAY_KHR).put(implLinux.getDisplay(peer_handle)); 282 break; 283 case LWJGLUtil.PLATFORM_MACOSX: 284 if (LWJGLUtil.isMacOSXEqualsOrBetterThan(10, 6)) { // only supported on OS X 10.6+ 285 // http://oscarbg.blogspot.com/2009/10/about-opencl-opengl-interop.html 286 final MacOSXContextImplementation implMacOSX = (MacOSXContextImplementation)implementation; 287 final long CGLShareGroup = implMacOSX.getCGLShareGroup(handle); 288 properties.put(APPLEGLSharing.CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE).put(CGLShareGroup); 289 break; 290 } 291 default: 292 throw new UnsupportedOperationException("CL/GL context sharing is not supported on this platform."); 293 } 294 } finally { 295 peer_info.unlock(); 296 } 297 } 298 299 }