00001 #region --- License ---
00002
00003
00004
00005 #endregion
00006
00007 using System;
00008 using System.Collections.Generic;
00009 using System.Text;
00010 using System.Runtime.InteropServices;
00011 using System.Diagnostics;
00012
00013 using OpenTK.Graphics;
00014
00015 namespace OpenTK.Platform.X11
00016 {
00022 internal sealed class X11GLContext : DesktopGraphicsContext
00023 {
00024 #region Fields
00025
00026
00027
00028
00029
00030 IntPtr display;
00031 X11WindowInfo currentWindow;
00032 bool vsync_supported;
00033 int vsync_interval;
00034 bool glx_loaded;
00035
00036 #endregion
00037
00038 #region --- Constructors ---
00039
00040 public X11GLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shared, bool direct,
00041 int major, int minor, GraphicsContextFlags flags)
00042 {
00043 if (mode == null)
00044 throw new ArgumentNullException("mode");
00045 if (window == null)
00046 throw new ArgumentNullException("window");
00047
00048 Mode = mode;
00049
00050
00051
00052 Display = ((X11WindowInfo)window).Display;
00053
00054 currentWindow = (X11WindowInfo)window;
00055 currentWindow.VisualInfo = SelectVisual(mode, currentWindow);
00056
00057 ContextHandle shareHandle = shared != null ?
00058 (shared as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero;
00059
00060 Debug.Write("Creating X11GLContext context: ");
00061 Debug.Write(direct ? "direct, " : "indirect, ");
00062 Debug.WriteLine(shareHandle.Handle == IntPtr.Zero ? "not shared... " :
00063 String.Format("shared with ({0})... ", shareHandle));
00064
00065 if (!glx_loaded)
00066 {
00067 Debug.WriteLine("Creating temporary context to load GLX extensions.");
00068
00069 // Create a temporary context to obtain the necessary function pointers.
00070 XVisualInfo visual = currentWindow.VisualInfo;
00071 IntPtr ctx = IntPtr.Zero;
00072
00073 using (new XLock(Display))
00074 {
00075 ctx = Glx.CreateContext(Display, ref visual, IntPtr.Zero, true);
00076 if (ctx == IntPtr.Zero)
00077 ctx = Glx.CreateContext(Display, ref visual, IntPtr.Zero, false);
00078 }
00079
00080 if (ctx != IntPtr.Zero)
00081 {
00082 new Glx().LoadEntryPoints();
00083 using (new XLock(Display))
00084 {
00085 Glx.MakeCurrent(Display, IntPtr.Zero, IntPtr.Zero);
00086 //Glx.DestroyContext(Display, ctx);
00087 }
00088 glx_loaded = true;
00089 }
00090 }
00091
00092 // Try using the new context creation method. If it fails, fall back to the old one.
00093 // For each of these methods, we try two times to create a context:
00094 // one with the "direct" flag intact, the other with the flag inversed.
00095 // HACK: It seems that Catalyst 9.1 - 9.4 on Linux have problems with contexts created through
00096 // GLX_ARB_create_context, including hideous input lag, no vsync and other. Use legacy context
00097 // creation if the user doesn't request a 3.0+ context.
00098 if ((major * 10 + minor >= 30) && Glx.Delegates.glXCreateContextAttribsARB != null)
00099 {
00100 Debug.Write("Using GLX_ARB_create_context... ");
00101
00102 unsafe
00103 {
00104 // We need the FB config for the current GraphicsMode.
00105 int count;
00106 IntPtr* fbconfigs = Glx.ChooseFBConfig(Display, currentWindow.Screen,
00107 new int[] {
00108 (int)GLXAttribute.VISUAL_ID,
00109 (int)mode.Index,
00110 0
00111 }, out count);
00112
00113 if (count > 0)
00114 {
00115 List<int> attributes = new List<int>();
00116 attributes.Add((int)ArbCreateContext.MajorVersion);
00117 attributes.Add(major);
00118 attributes.Add((int)ArbCreateContext.MinorVersion);
00119 attributes.Add(minor);
00120 if (flags != 0)
00121 {
00122 #warning "This is not entirely correct: Embedded is not a valid flag! We need to add a GetARBContextFlags(GraphicsContextFlags) method."
00123 attributes.Add((int)ArbCreateContext.Flags);
00124 attributes.Add((int)flags);
00125 }
00126 // According to the docs, " <attribList> specifies a list of attributes for the context.
00127
00128
00129
00130 attributes.Add(0);
00131 attributes.Add(0);
00132
00133 using (new XLock(Display))
00134 {
00135 Handle = new ContextHandle(Glx.Arb.CreateContextAttribs(Display, *fbconfigs,
00136 shareHandle.Handle, direct, attributes.ToArray()));
00137
00138 if (Handle == ContextHandle.Zero)
00139 {
00140 Debug.Write(String.Format("failed. Trying direct: {0}... ", !direct));
00141 Handle = new ContextHandle(Glx.Arb.CreateContextAttribs(Display, *fbconfigs,
00142 shareHandle.Handle, !direct, attributes.ToArray()));
00143 }
00144 }
00145
00146 if (Handle == ContextHandle.Zero)
00147 Debug.WriteLine("failed.");
00148 else
00149 Debug.WriteLine("success!");
00150
00151 using (new XLock(Display))
00152 {
00153 Functions.XFree((IntPtr)fbconfigs);
00154 }
00155 }
00156 }
00157 }
00158
00159 if (Handle == ContextHandle.Zero)
00160 {
00161 Debug.Write("Using legacy context creation... ");
00162
00163 XVisualInfo info = currentWindow.VisualInfo;
00164 using (new XLock(Display))
00165 {
00166
00167 Handle = new ContextHandle(Glx.CreateContext(Display, ref info, shareHandle.Handle, direct));
00168
00169 if (Handle == ContextHandle.Zero)
00170 {
00171 Debug.WriteLine(String.Format("failed. Trying direct: {0}... ", !direct));
00172 Handle = new ContextHandle(Glx.CreateContext(Display, ref info, IntPtr.Zero, !direct));
00173 }
00174 }
00175 }
00176
00177 if (Handle != ContextHandle.Zero)
00178 Debug.Print("Context created (id: {0}).", Handle);
00179 else
00180 throw new GraphicsContextException("Failed to create OpenGL context. Glx.CreateContext call returned 0.");
00181
00182 using (new XLock(Display))
00183 {
00184 if (!Glx.IsDirect(Display, Handle.Handle))
00185 Debug.Print("Warning: Context is not direct.");
00186 }
00187 }
00188
00189 public X11GLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shared, bool direct,
00190 int major, int minor, GraphicsContextFlags flags)
00191 {
00192 if (handle == ContextHandle.Zero)
00193 throw new ArgumentException("handle");
00194 if (window == null)
00195 throw new ArgumentNullException("window");
00196
00197 Handle = handle;
00198 currentWindow = (X11WindowInfo)window;
00199 Display = currentWindow.Display;
00200 }
00201
00202 #endregion
00203
00204 #region --- Private Methods ---
00205
00206 IntPtr Display
00207 {
00208 get { return display; }
00209 set
00210 {
00211 if (value == IntPtr.Zero)
00212 throw new ArgumentOutOfRangeException();
00213 if (display != IntPtr.Zero)
00214 throw new InvalidOperationException("The display connection may not be changed after being set.");
00215 display = value;
00216 }
00217 }
00218
00219 #region XVisualInfo SelectVisual(GraphicsMode mode, X11WindowInfo currentWindow)
00220
00221 XVisualInfo SelectVisual(GraphicsMode mode, X11WindowInfo currentWindow)
00222 {
00223 XVisualInfo info = new XVisualInfo();
00224 info.VisualID = (IntPtr)mode.Index;
00225 info.Screen = currentWindow.Screen;
00226 int items;
00227
00228 lock (API.Lock)
00229 {
00230 IntPtr vs = Functions.XGetVisualInfo(Display, XVisualInfoMask.ID | XVisualInfoMask.Screen, ref info, out items);
00231 if (items == 0)
00232 throw new GraphicsModeException(String.Format("Invalid GraphicsMode specified ({0}).", mode));
00233
00234 info = (XVisualInfo)Marshal.PtrToStructure(vs, typeof(XVisualInfo));
00235 Functions.XFree(vs);
00236 }
00237
00238 return info;
00239 }
00240
00241 #endregion
00242
00243 bool SupportsExtension(X11WindowInfo window, string e)
00244 {
00245 if (window == null)
00246 throw new ArgumentNullException("window");
00247 if (e == null)
00248 throw new ArgumentNullException("e");
00249 if (window.Display != Display)
00250 throw new InvalidOperationException();
00251
00252 string extensions = null;
00253 using (new XLock(Display))
00254 {
00255 extensions = Glx.QueryExtensionsString(Display, window.Screen);
00256 }
00257 return !String.IsNullOrEmpty(extensions) && extensions.Contains(e);
00258 }
00259
00260 #endregion
00261
00262 #region --- IGraphicsContext Members ---
00263
00264 #region SwapBuffers()
00265
00266 public override void SwapBuffers()
00267 {
00268 if (Display == IntPtr.Zero || currentWindow.WindowHandle == IntPtr.Zero)
00269 throw new InvalidOperationException(
00270 String.Format("Window is invalid. Display ({0}), Handle ({1}).", Display, currentWindow.WindowHandle));
00271 using (new XLock(Display))
00272 {
00273 Glx.SwapBuffers(Display, currentWindow.WindowHandle);
00274 }
00275 }
00276
00277 #endregion
00278
00279 #region MakeCurrent
00280
00281 public override void MakeCurrent(IWindowInfo window)
00282 {
00283 if (window == currentWindow && IsCurrent)
00284 return;
00285
00286 if (window != null && ((X11WindowInfo)window).Display != Display)
00287 throw new InvalidOperationException("MakeCurrent() may only be called on windows originating from the same display that spawned this GL context.");
00288
00289 if (window == null)
00290 {
00291 Debug.Write(String.Format("Releasing context {0} from thread {1} (Display: {2})... ",
00292 Handle, System.Threading.Thread.CurrentThread.ManagedThreadId, Display));
00293
00294 bool result;
00295 using (new XLock(Display))
00296 {
00297 result = Glx.MakeCurrent(Display, IntPtr.Zero, IntPtr.Zero);
00298 if (result)
00299 {
00300 currentWindow = null;
00301 }
00302 }
00303 Debug.Print("{0}", result ? "done!" : "failed.");
00304 }
00305 else
00306 {
00307 X11WindowInfo w = (X11WindowInfo)window;
00308 bool result;
00309
00310 Debug.Write(String.Format("Making context {0} current on thread {1} (Display: {2}, Screen: {3}, Window: {4})... ",
00311 Handle, System.Threading.Thread.CurrentThread.ManagedThreadId, Display, w.Screen, w.WindowHandle));
00312
00313 if (Display == IntPtr.Zero || w.WindowHandle == IntPtr.Zero || Handle == ContextHandle.Zero)
00314 throw new InvalidOperationException("Invalid display, window or context.");
00315
00316 using (new XLock(Display))
00317 {
00318 result = Glx.MakeCurrent(Display, w.WindowHandle, Handle);
00319 if (result)
00320 {
00321 currentWindow = w;
00322 }
00323 }
00324
00325 if (!result)
00326 throw new GraphicsContextException("Failed to make context current.");
00327 else
00328 Debug.WriteLine("done!");
00329 }
00330
00331 currentWindow = (X11WindowInfo)window;
00332 }
00333
00334 #endregion
00335
00336 #region IsCurrent
00337
00338 public override bool IsCurrent
00339 {
00340 get
00341 {
00342 using (new XLock(Display))
00343 {
00344 return Glx.GetCurrentContext() == Handle.Handle;
00345 }
00346 }
00347 }
00348
00349 #endregion
00350
00351 #region VSync
00352
00353 public override bool VSync
00354 {
00355 get
00356 {
00357 return vsync_supported && vsync_interval != 0;
00358 }
00359 set
00360 {
00361 if (vsync_supported)
00362 {
00363 ErrorCode error_code = 0;
00364 using (new XLock(Display))
00365 {
00366 error_code = Glx.Sgi.SwapInterval(value ? 1 : 0);
00367 }
00368 if (error_code != X11.ErrorCode.NO_ERROR)
00369 Debug.Print("VSync = {0} failed, error code: {1}.", value, error_code);
00370 vsync_interval = value ? 1 : 0;
00371 }
00372 }
00373 }
00374
00375 #endregion
00376
00377 #region GetAddress
00378
00379 public override IntPtr GetAddress(string function)
00380 {
00381 using (new XLock(Display))
00382 {
00383 return Glx.GetProcAddress(function);
00384 }
00385 }
00386
00387 #endregion
00388
00389 #endregion
00390
00391 #region --- IGLContextInternal Members ---
00392
00393 #region LoadAll
00394
00395 public override void LoadAll()
00396 {
00397 new Glx().LoadEntryPoints();
00398 vsync_supported = this.GetAddress("glXSwapIntervalSGI") != IntPtr.Zero;
00399 Debug.Print("Context supports vsync: {0}.", vsync_supported);
00400
00401 base.LoadAll();
00402 }
00403
00404 #endregion
00405
00406 #region IWindowInfo IGLContextInternal.Info
00407
00408
00409
00410 #endregion
00411
00412 #endregion
00413
00414 #region --- IDisposable Members ---
00415
00416 public override void Dispose()
00417 {
00418 this.Dispose(true);
00419 GC.SuppressFinalize(this);
00420 }
00421
00422 private void Dispose(bool manuallyCalled)
00423 {
00424 if (!IsDisposed)
00425 {
00426 if (manuallyCalled)
00427 {
00428 IntPtr display = Display;
00429
00430 if (IsCurrent)
00431 {
00432 using (new XLock(display))
00433 {
00434 Glx.MakeCurrent(display, IntPtr.Zero, IntPtr.Zero);
00435 }
00436 }
00437 using (new XLock(display))
00438 {
00439 Glx.DestroyContext(display, Handle);
00440 }
00441 }
00442 }
00443 else
00444 {
00445 Debug.Print("[Warning] {0} leaked.", this.GetType().Name);
00446 }
00447 IsDisposed = true;
00448 }
00449
00450
00451 ~X11GLContext()
00452 {
00453 this.Dispose(false);
00454 }
00455
00456 #endregion
00457 }
00458 }