diff --git a/Source/OpenTK/Platform/X11/Bindings/Glx.cs b/Source/OpenTK/Platform/X11/Bindings/Glx.cs index 27c83695..f6b5bb68 100644 --- a/Source/OpenTK/Platform/X11/Bindings/Glx.cs +++ b/Source/OpenTK/Platform/X11/Bindings/Glx.cs @@ -134,6 +134,8 @@ namespace OpenTK.Platform.X11 LEVEL = 3, CONFIG_CAVEAT = 0x20, RENDER_TYPE_SGIX = 0x8011, + SWAP_INTERVAL_EXT = 0x20F1, + MAX_SWAP_INTERVAL_EXT = 0x20F2, } enum GLXHyperpipeAttrib : int @@ -259,32 +261,69 @@ namespace OpenTK.Platform.X11 /// /// Provides access to GLX functions. /// - class Glx + class Glx : Graphics.GraphicsBindingsBase { const string Library = "libGL.so.1"; + static readonly object sync_root = new object(); - static string[] EntryPointNames = new string[] + static readonly byte[] EntryPointNames = new byte[] { - "glXCreateContextAttribs", - "glXSwapIntervalEXT", - "glXGetSwapIntervalEXT", - "glXSwapIntervalMESA", - "glXGetSwapIntervalMESA", - "glXSwapIntervalOML", - "glXGetSwapIntervalOML", - "glXSwapIntervalSGI", - "glXGetSwapIntervalSGI", + // glXCreateContextAttribsARB + 0x67, 0x6c, 0x58, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x73, 0x41, 0x52, 0x42, 0, + // glXSwapIntervalEXT + 0x67, 0x6c, 0x58, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x45, 0x58, 0x54, 0, + // glXSwapIntervalMESA + 0x67, 0x6c, 0x58, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x45, 0x53, 0x41, 0, + // glXGetSwapIntervalMESA + 0x67, 0x6c, 0x58, 0x47, 0x65, 0x74, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x45, 0x53, 0x41, 0, + // glXSwapIntervalSGI + 0x67, 0x6c, 0x58, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x47, 0x49, 0, }; - static IntPtr[] EntryPoints = new IntPtr[EntryPointNames.Length]; + static readonly int[] EntryPointOffsets = new int[5]; + static IntPtr[] EntryPoints = new IntPtr[5]; - static Glx() + internal Glx() { // GLX entry points are not bound to a context. // This means we can load them without creating // a context first! (unlike WGL) - for (int i = 0; i < EntryPointNames.Length; i++) + _EntryPointsInstance = EntryPoints; + _EntryPointNamesInstance = EntryPointNames; + _EntryPointNameOffsetsInstance = EntryPointOffsets; + + // Writing the entry point name offsets + // by hand is error prone. Do it in code + // instead: + int offset = 0; + for (int i = 0, j = 0; i < EntryPointNames.Length; i++) { - EntryPoints[i] = Arb.GetProcAddress(EntryPointNames[i]); + if (EntryPointNames[i] == 0) + { + EntryPointOffsets[j++] = offset; + offset = i + 1; + } + } + } + + protected override object SyncRoot { get { return sync_root; } } + + protected override IntPtr GetAddress(string funcname) + { + return Arb.GetProcAddress(funcname); + } + + internal override void LoadEntryPoints() + { + unsafe + { + fixed (byte* name = _EntryPointNamesInstance) + { + for (int i = 0; i < _EntryPointsInstance.Length; i++) + { + _EntryPointsInstance[i] = Arb.GetProcAddress( + new IntPtr(name + _EntryPointNameOffsetsInstance[i])); + } + } } } @@ -302,6 +341,9 @@ namespace OpenTK.Platform.X11 [DllImport(Library, EntryPoint = "glXIsDirect")] public static extern bool IsDirect(IntPtr dpy, IntPtr context); + + [DllImport(Library, EntryPoint = "glXQueryDrawable")] + public static extern ErrorCode QueryDrawable(IntPtr dpy, IntPtr drawable, GLXAttribute attribute, out int value); [DllImport(Library, EntryPoint = "glXQueryExtension")] public static extern bool QueryExtension(IntPtr dpy, ref int errorBase, ref int eventBase); @@ -409,19 +451,16 @@ namespace OpenTK.Platform.X11 [DllImport(Library, EntryPoint = "glXGetProcAddressARB")] public static extern IntPtr GetProcAddress([MarshalAs(UnmanagedType.LPTStr)] string procName); + [DllImport(Library, EntryPoint = "glXGetProcAddressARB")] + public static extern IntPtr GetProcAddress(IntPtr procName); + #endregion } public partial class Ext { [AutoGenerated(EntryPoint = "glXSwapIntervalEXT")] - public static ErrorCode SwapInterval(int interval) - { - throw new NotImplementedException(); - } - - [AutoGenerated(EntryPoint = "glXGetSwapIntervalEXT")] - public static int GetSwapInterval() + public static ErrorCode SwapInterval(IntPtr display, IntPtr drawable, int interval) { throw new NotImplementedException(); } @@ -449,35 +488,23 @@ namespace OpenTK.Platform.X11 { throw new NotImplementedException(); } - - [AutoGenerated(EntryPoint = "glXGetSwapIntervalSGI")] - public static int GetSwapInterval() - { - throw new NotImplementedException(); - } } [Slot(0)] - [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] internal unsafe static extern IntPtr glXCreateContextAttribsARB(IntPtr display, IntPtr fbconfig, IntPtr share_context, bool direct, int* attribs); [Slot(1)] - [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] - internal static extern ErrorCode glXSwapIntervalEXT(int interval); + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern ErrorCode glXSwapIntervalEXT(IntPtr display, IntPtr drawable, int interval); [Slot(2)] - [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] - internal static extern int glXGetSwapIntervalEXT(); - [Slot(3)] - [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] internal static extern ErrorCode glXSwapIntervalMESA(int interval); - [Slot(4)] - [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + [Slot(3)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] internal static extern int glXGetSwapIntervalMESA(); - [Slot(5)] - [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] + [Slot(4)] + [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] internal static extern ErrorCode glXSwapIntervalSGI(int interval); - [Slot(6)] - [DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] - internal static extern int glXGetSwapIntervalSGI(); #endregion } diff --git a/Source/OpenTK/Platform/X11/X11GLContext.cs b/Source/OpenTK/Platform/X11/X11GLContext.cs index 9596f0e3..ca77aebc 100644 --- a/Source/OpenTK/Platform/X11/X11GLContext.cs +++ b/Source/OpenTK/Platform/X11/X11GLContext.cs @@ -41,6 +41,11 @@ namespace OpenTK.Platform.X11 #region --- Constructors --- + static X11GLContext() + { + new Glx().LoadEntryPoints(); + } + public X11GLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shared, bool direct, int major, int minor, GraphicsContextFlags flags) { @@ -258,8 +263,7 @@ namespace OpenTK.Platform.X11 { return SupportsExtension(display, window, "GLX_ARB_create_context") && - SupportsExtension(display, window, "GLX_ARB_create_context_profile") && - Glx.SupportsFunction("glXCreateContextAttribsARB"); + SupportsExtension(display, window, "GLX_ARB_create_context_profile"); } #endregion @@ -359,20 +363,40 @@ namespace OpenTK.Platform.X11 { get { + if (currentWindow == null) + { + Debug.Print("Context must be current"); + throw new InvalidOperationException(); + } + using (new XLock(display)) { if (vsync_ext_supported) - return Glx.Ext.GetSwapInterval(); + { + int value; + Glx.QueryDrawable(Display, currentWindow.Handle, GLXAttribute.SWAP_INTERVAL_EXT, out value); + return value; + } else if (vsync_mesa_supported) + { return Glx.Mesa.GetSwapInterval(); + } else if (vsync_sgi_supported) + { return sgi_swap_interval; - else - return 0; + } + + return 0; } } set { + if (currentWindow == null) + { + Debug.Print("Context must be current"); + throw new InvalidOperationException(); + } + if (value < 0 && !vsync_tear_supported) { value = 1; @@ -382,11 +406,17 @@ namespace OpenTK.Platform.X11 using (new XLock(Display)) { if (vsync_ext_supported) - error_code = Glx.Ext.SwapInterval(value); + { + Glx.Ext.SwapInterval(Display, currentWindow.Handle, value); + } else if (vsync_mesa_supported) + { error_code = Glx.Mesa.SwapInterval(value); + } else if (vsync_sgi_supported) + { error_code = Glx.Sgi.SwapInterval(value); + } } if (error_code == X11.ErrorCode.NO_ERROR) @@ -402,23 +432,24 @@ namespace OpenTK.Platform.X11 public override void LoadAll() { + // Note: GLX entry points are always available, even + // for extensions that are not currently supported by + // the underlying driver. This means we can only check + // the extension strings for support, not the entry + // points themselves. vsync_ext_supported = - SupportsExtension(display, currentWindow, "GLX_EXT_swap_control") && - Glx.SupportsFunction("glXSwapIntervalEXT") && - Glx.SupportsFunction("glXGetSwapIntervalEXT"); + SupportsExtension(display, currentWindow, "GLX_EXT_swap_control"); vsync_mesa_supported = - SupportsExtension(display, currentWindow, "GLX_MESA_swap_control") && - Glx.SupportsFunction("glXSwapIntervalMESA") && - Glx.SupportsFunction("glXGetSwapIntervalMESA"); + SupportsExtension(display, currentWindow, "GLX_MESA_swap_control"); vsync_sgi_supported = - SupportsExtension(display, currentWindow, "GLX_SGI_swap_control") && - Glx.SupportsFunction("glXSwapIntervalSGI"); - Debug.Print("Context supports vsync: {0}.", - vsync_ext_supported || vsync_mesa_supported || vsync_ext_supported); - + SupportsExtension(display, currentWindow, "GLX_SGI_swap_control"); vsync_tear_supported = SupportsExtension(display, currentWindow, "GLX_EXT_swap_control_tear"); - Debug.Print("Context supports vsync tear: {0}.", vsync_tear_supported); + + Debug.Print("Context supports vsync: {0}.", + vsync_ext_supported || vsync_mesa_supported || vsync_sgi_supported); + Debug.Print("Context supports adaptive vsync: {0}.", + vsync_tear_supported); base.LoadAll(); }