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 }