[X11] Fix for issue #143

OpenTK will now use the same GLXFBConfig to create the INativeWindow
and IGraphicsContext on Linux/X11. This resolves an issue where OpenGL
3.x contexts could not be created on some graphics drivers (e.g.
nvidia binary.)
This commit is contained in:
Stefanos A. 2014-07-19 20:39:17 +02:00
parent a13a2f8e66
commit d75a2ce439
5 changed files with 199 additions and 206 deletions

View file

@ -248,11 +248,7 @@ namespace OpenTK.Platform
window.Screen = screen;
window.Handle = windowHandle;
window.RootWindow = rootWindow;
if (visualInfo != IntPtr.Zero)
{
window.VisualInfo = (X11.XVisualInfo)Marshal.PtrToStructure(visualInfo, typeof(X11.XVisualInfo));
}
window.Visual = visualInfo;
return window;
}

View file

@ -78,13 +78,26 @@ namespace OpenTK.Platform.X11
}
}
Mode = ModeSelector.SelectGraphicsMode(
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo);
IntPtr visual = IntPtr.Zero;
IntPtr fbconfig = IntPtr.Zero;
// Once a window has a visual, we cannot use a different
// visual on the OpenGL context, or glXMakeCurrent might fail.
// Note: we should only check X11WindowInfo.Visual, as that
// is the only property that can be set by Utilities.CreateX11WindowInfo.
currentWindow = (X11WindowInfo)window;
currentWindow.VisualInfo = SelectVisual(Mode, currentWindow);
if (currentWindow.Visual != IntPtr.Zero)
{
visual = currentWindow.Visual;
fbconfig = currentWindow.FBConfig;
Mode = currentWindow.GraphicsMode;
}
if (!Mode.Index.HasValue)
{
Mode = ModeSelector.SelectGraphicsMode(mode, out visual, out fbconfig);
}
ContextHandle shareHandle = shared != null ?
(shared as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero;
@ -99,84 +112,15 @@ namespace OpenTK.Platform.X11
// HACK: It seems that Catalyst 9.1 - 9.4 on Linux have problems with contexts created through
// GLX_ARB_create_context, including hideous input lag, no vsync and other madness.
// Use legacy context creation if the user doesn't request a 3.0+ context.
if ((major * 10 + minor >= 30) && SupportsCreateContextAttribs(Display, currentWindow))
if (fbconfig != IntPtr.Zero && (major * 10 + minor >= 30) && SupportsCreateContextAttribs(Display, currentWindow))
{
Debug.Write("Using GLX_ARB_create_context... ");
unsafe
{
// We need the FB config for the current GraphicsMode.
int count;
IntPtr* fbconfigs = Glx.ChooseFBConfig(Display, currentWindow.Screen,
new int[] {
(int)GLXAttribute.VISUAL_ID,
(int)Mode.Index,
0
}, out count);
if (count > 0)
{
List<int> attributes = new List<int>();
attributes.Add((int)ArbCreateContext.MajorVersion);
attributes.Add(major);
attributes.Add((int)ArbCreateContext.MinorVersion);
attributes.Add(minor);
if (flags != 0)
{
attributes.Add((int)ArbCreateContext.Flags);
attributes.Add((int)GetARBContextFlags(flags));
attributes.Add((int)ArbCreateContext.ProfileMask);
attributes.Add((int)GetARBProfileFlags(flags));
}
// According to the docs, " <attribList> specifies a list of attributes for the context.
// The list consists of a sequence of <name,value> pairs terminated by the
// value 0. [...]"
// Is this a single 0, or a <0, 0> pair? (Defensive coding: add two zeroes just in case).
attributes.Add(0);
attributes.Add(0);
using (new XLock(Display))
{
Handle = new ContextHandle(Glx.Arb.CreateContextAttribs(Display, *fbconfigs,
shareHandle.Handle, direct, attributes.ToArray()));
if (Handle == ContextHandle.Zero)
{
Debug.Write(String.Format("failed. Trying direct: {0}... ", !direct));
Handle = new ContextHandle(Glx.Arb.CreateContextAttribs(Display, *fbconfigs,
shareHandle.Handle, !direct, attributes.ToArray()));
}
}
if (Handle == ContextHandle.Zero)
Debug.WriteLine("failed.");
else
Debug.WriteLine("success!");
using (new XLock(Display))
{
Functions.XFree((IntPtr)fbconfigs);
}
}
}
Handle = CreateContextAttribs(Display, currentWindow.Screen,
fbconfig, direct, major, minor, flags, shareHandle);
}
if (Handle == ContextHandle.Zero)
{
Debug.Write("Using legacy context creation... ");
XVisualInfo info = currentWindow.VisualInfo;
using (new XLock(Display))
{
// Cannot pass a Property by reference.
Handle = new ContextHandle(Glx.CreateContext(Display, ref info, shareHandle.Handle, direct));
if (Handle == ContextHandle.Zero)
{
Debug.WriteLine(String.Format("failed. Trying direct: {0}... ", !direct));
Handle = new ContextHandle(Glx.CreateContext(Display, ref info, IntPtr.Zero, !direct));
}
}
Handle = CreateContextLegacy(Display, currentWindow.VisualInfo, direct, shareHandle);
}
if (Handle != ContextHandle.Zero)
@ -208,6 +152,74 @@ namespace OpenTK.Platform.X11
#region --- Private Methods ---
static ContextHandle CreateContextAttribs(
IntPtr display, int screen, IntPtr fbconfig,
bool direct, int major, int minor,
GraphicsContextFlags flags, ContextHandle shareContext)
{
Debug.Write("Using GLX_ARB_create_context... ");
IntPtr context = IntPtr.Zero;
{
// We need the FB config for the current GraphicsMode.
List<int> attributes = new List<int>();
attributes.Add((int)ArbCreateContext.MajorVersion);
attributes.Add(major);
attributes.Add((int)ArbCreateContext.MinorVersion);
attributes.Add(minor);
if (flags != 0)
{
attributes.Add((int)ArbCreateContext.Flags);
attributes.Add((int)GetARBContextFlags(flags));
attributes.Add((int)ArbCreateContext.ProfileMask);
attributes.Add((int)GetARBProfileFlags(flags));
}
// According to the docs, " <attribList> specifies a list of attributes for the context.
// The list consists of a sequence of <name,value> pairs terminated by the
// value 0. [...]"
// Is this a single 0, or a <0, 0> pair? (Defensive coding: add two zeroes just in case).
attributes.Add(0);
attributes.Add(0);
using (new XLock(display))
{
context = Glx.Arb.CreateContextAttribs(display, fbconfig, shareContext.Handle, direct, attributes.ToArray());
if (context == IntPtr.Zero)
{
Debug.Write(String.Format("failed. Trying direct: {0}... ", !direct));
context = Glx.Arb.CreateContextAttribs(display, fbconfig, shareContext.Handle, !direct, attributes.ToArray());
}
}
if (context == IntPtr.Zero)
Debug.WriteLine("failed.");
else
Debug.WriteLine("success!");
}
return new ContextHandle(context);
}
static ContextHandle CreateContextLegacy(IntPtr display,
XVisualInfo info, bool direct, ContextHandle shareContext)
{
Debug.Write("Using legacy context creation... ");
IntPtr context;
using (new XLock(display))
{
// Cannot pass a Property by reference.
context = Glx.CreateContext(display, ref info, shareContext.Handle, direct);
if (context == IntPtr.Zero)
{
Debug.WriteLine(String.Format("failed. Trying direct: {0}... ", !direct));
context = Glx.CreateContext(display, ref info, IntPtr.Zero, !direct);
}
}
return new ContextHandle(context);
}
IntPtr Display
{
get { return display; }
@ -221,38 +233,14 @@ namespace OpenTK.Platform.X11
}
}
#region XVisualInfo SelectVisual(GraphicsMode mode, X11WindowInfo currentWindow)
XVisualInfo SelectVisual(GraphicsMode mode, X11WindowInfo currentWindow)
{
XVisualInfo info = new XVisualInfo();
info.VisualID = (IntPtr)mode.Index;
info.Screen = currentWindow.Screen;
int items;
lock (API.Lock)
{
IntPtr vs = Functions.XGetVisualInfo(Display, XVisualInfoMask.ID | XVisualInfoMask.Screen, ref info, out items);
if (items == 0)
throw new GraphicsModeException(String.Format("Invalid GraphicsMode specified ({0}).", mode));
info = (XVisualInfo)Marshal.PtrToStructure(vs, typeof(XVisualInfo));
Functions.XFree(vs);
}
return info;
}
#endregion
ArbCreateContext GetARBContextFlags(GraphicsContextFlags flags)
static ArbCreateContext GetARBContextFlags(GraphicsContextFlags flags)
{
ArbCreateContext result = 0;
result |= (flags & GraphicsContextFlags.Debug) != 0 ? ArbCreateContext.DebugBit : 0;
return result;
}
ArbCreateContext GetARBProfileFlags(GraphicsContextFlags flags)
static ArbCreateContext GetARBProfileFlags(GraphicsContextFlags flags)
{
ArbCreateContext result = 0;
result |= (flags & GraphicsContextFlags.ForwardCompatible) != 0 ?

View file

@ -140,7 +140,7 @@ namespace OpenTK.Platform.X11
#region Constructors
public X11GLNative(int x, int y, int width, int height, string title,
GraphicsMode mode,GameWindowFlags options, DisplayDevice device)
GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
: this()
{
if (width <= 0)
@ -154,17 +154,13 @@ namespace OpenTK.Platform.X11
using (new XLock(window.Display))
{
if (!mode.Index.HasValue)
{
mode = new X11GraphicsMode().SelectGraphicsMode(
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo);
}
IntPtr visual;
IntPtr fbconfig;
window.GraphicsMode = new X11GraphicsMode()
.SelectGraphicsMode(mode, out visual, out fbconfig);
info.VisualID = mode.Index.Value;
int dummy;
window.VisualInfo = (XVisualInfo)Marshal.PtrToStructure(
Functions.XGetVisualInfo(window.Display, XVisualInfoMask.ID, ref info, out dummy), typeof(XVisualInfo));
window.Visual = visual;
window.FBConfig = fbconfig;
// Create a window on this display using the visual above
Debug.Write("Opening render window... ");

View file

@ -16,7 +16,7 @@ using OpenTK.Graphics;
namespace OpenTK.Platform.X11
{
class X11GraphicsMode : IGraphicsMode
class X11GraphicsMode
{
// Todo: Add custom visual selection algorithm, instead of ChooseFBConfig/ChooseVisual.
// It seems the Choose* methods do not take multisampling into account (at least on some
@ -32,34 +32,45 @@ namespace OpenTK.Platform.X11
#region IGraphicsMode Members
public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples, ColorFormat accum,
int buffers, bool stereo)
public GraphicsMode SelectGraphicsMode(GraphicsMode desired_mode, out IntPtr visual, out IntPtr fbconfig)
{
GraphicsMode gfx;
// The actual GraphicsMode that will be selected.
IntPtr visual = IntPtr.Zero;
GraphicsMode mode = new GraphicsMode(desired_mode);
visual = IntPtr.Zero;
fbconfig = IntPtr.Zero;
IntPtr display = API.DefaultDisplay;
do
{
// Try to select a visual using Glx.ChooseFBConfig and Glx.GetVisualFromFBConfig.
// This is only supported on GLX 1.3 - if it fails, fall back to Glx.ChooseVisual.
visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo);
fbconfig = SelectFBConfig(mode);
if (fbconfig != IntPtr.Zero)
visual = Glx.GetVisualFromFBConfig(display, fbconfig);
if (visual == IntPtr.Zero)
visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo);
visual = SelectVisual(mode);
if (visual == IntPtr.Zero)
{
// Relax parameters and retry
if (!Utilities.RelaxGraphicsMode(ref color, ref depth, ref stencil, ref samples, ref accum, ref buffers, ref stereo))
if (!Utilities.RelaxGraphicsMode(ref mode))
throw new GraphicsModeException("Requested GraphicsMode not available.");
}
}
while (visual == IntPtr.Zero);
XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo));
gfx = CreateGraphicsMode(display, ref info);
return gfx;
}
#endregion
#region Private Members
static GraphicsMode CreateGraphicsMode(IntPtr display, ref XVisualInfo info)
{
// See what we *really* got:
int r, g, b, a;
Glx.GetConfig(display, ref info, GLXAttribute.ALPHA_SIZE, out a);
@ -71,99 +82,84 @@ namespace OpenTK.Platform.X11
Glx.GetConfig(display, ref info, GLXAttribute.ACCUM_RED_SIZE, out ar);
Glx.GetConfig(display, ref info, GLXAttribute.ACCUM_GREEN_SIZE, out ag);
Glx.GetConfig(display, ref info, GLXAttribute.ACCUM_BLUE_SIZE, out ab);
int depth, stencil, samples, buffers;
Glx.GetConfig(display, ref info, GLXAttribute.DEPTH_SIZE, out depth);
Glx.GetConfig(display, ref info, GLXAttribute.STENCIL_SIZE, out stencil);
Glx.GetConfig(display, ref info, GLXAttribute.SAMPLES, out samples);
Glx.GetConfig(display, ref info, GLXAttribute.DOUBLEBUFFER, out buffers);
++buffers;
// the above lines returns 0 - false and 1 - true.
int st;
Glx.GetConfig(display, ref info, GLXAttribute.STEREO, out st);
stereo = st != 0;
gfx = new GraphicsMode(info.VisualID, new ColorFormat(r, g, b, a), depth, stencil, samples,
new ColorFormat(ar, ag, ab, aa), buffers, stereo);
using (new XLock(display))
{
Functions.XFree(visual);
}
return gfx;
// Note: Glx.GetConfig return buffers = 0 (false) or 1 (true).
// OpenTK expects buffers = 1 (single-) or 2 (double-buffering),
// so increase the GLX value by one.
return new GraphicsMode(info.VisualID, new ColorFormat(r, g, b, a), depth, stencil, samples,
new ColorFormat(ar, ag, ab, aa), buffers + 1, st != 0);
}
#endregion
#region Private Members
// See http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.opengl/doc/openglrf/glXChooseFBConfig.htm
// for the attribute declarations. Note that the attributes are different than those used in Glx.ChooseVisual.
IntPtr SelectVisualUsingFBConfig(ColorFormat color, int depth, int stencil, int samples, ColorFormat accum,
int buffers, bool stereo)
IntPtr SelectFBConfig(GraphicsMode mode)
{
Debug.Print("Selecting FB config for {0}", mode);
List<int> visualAttributes = new List<int>();
IntPtr visual = IntPtr.Zero;
Debug.Print("Bits per pixel: {0}", color.BitsPerPixel);
if (color.BitsPerPixel > 0)
if (mode.ColorFormat.BitsPerPixel > 0)
{
if (!color.IsIndexed)
if (!mode.ColorFormat.IsIndexed)
{
visualAttributes.Add((int)GLXAttribute.RGBA);
visualAttributes.Add(1);
visualAttributes.Add((int)GLXAttribute.RENDER_TYPE);
visualAttributes.Add((int)GLXRenderTypeMask.RGBA_BIT);
}
visualAttributes.Add((int)GLXAttribute.RED_SIZE);
visualAttributes.Add(color.Red);
visualAttributes.Add(mode.ColorFormat.Red);
visualAttributes.Add((int)GLXAttribute.GREEN_SIZE);
visualAttributes.Add(color.Green);
visualAttributes.Add(mode.ColorFormat.Green);
visualAttributes.Add((int)GLXAttribute.BLUE_SIZE);
visualAttributes.Add(color.Blue);
visualAttributes.Add(mode.ColorFormat.Blue);
visualAttributes.Add((int)GLXAttribute.ALPHA_SIZE);
visualAttributes.Add(color.Alpha);
visualAttributes.Add(mode.ColorFormat.Alpha);
}
Debug.Print("Depth: {0}", depth);
if (depth > 0)
if (mode.Depth > 0)
{
visualAttributes.Add((int)GLXAttribute.DEPTH_SIZE);
visualAttributes.Add(depth);
visualAttributes.Add(mode.Depth);
}
if (buffers > 1)
if (mode.Buffers > 1)
{
visualAttributes.Add((int)GLXAttribute.DOUBLEBUFFER);
visualAttributes.Add(1);
}
if (stencil > 1)
if (mode.Stereo)
{
visualAttributes.Add((int)GLXAttribute.STENCIL_SIZE);
visualAttributes.Add(stencil);
visualAttributes.Add(mode.Stereo ? 1 : 0);
}
if (accum.BitsPerPixel > 0)
if (mode.AccumulatorFormat.BitsPerPixel > 0)
{
visualAttributes.Add((int)GLXAttribute.ACCUM_ALPHA_SIZE);
visualAttributes.Add(accum.Alpha);
visualAttributes.Add(mode.AccumulatorFormat.Alpha);
visualAttributes.Add((int)GLXAttribute.ACCUM_BLUE_SIZE);
visualAttributes.Add(accum.Blue);
visualAttributes.Add(mode.AccumulatorFormat.Blue);
visualAttributes.Add((int)GLXAttribute.ACCUM_GREEN_SIZE);
visualAttributes.Add(accum.Green);
visualAttributes.Add(mode.AccumulatorFormat.Green);
visualAttributes.Add((int)GLXAttribute.ACCUM_RED_SIZE);
visualAttributes.Add(accum.Red);
visualAttributes.Add(mode.AccumulatorFormat.Red);
}
if (samples > 0)
if (mode.Samples > 0)
{
visualAttributes.Add((int)GLXAttribute.SAMPLE_BUFFERS);
visualAttributes.Add(1);
visualAttributes.Add((int)GLXAttribute.SAMPLES);
visualAttributes.Add(samples);
visualAttributes.Add(mode.Samples);
}
if (stereo)
if (mode.Stereo)
{
visualAttributes.Add((int)GLXAttribute.STEREO);
visualAttributes.Add(1);
@ -173,6 +169,7 @@ namespace OpenTK.Platform.X11
// Select a visual that matches the parameters set by the user.
IntPtr display = API.DefaultDisplay;
IntPtr result = IntPtr.Zero;
using (new XLock(display))
{
try
@ -180,7 +177,7 @@ namespace OpenTK.Platform.X11
int screen = Functions.XDefaultScreen(display);
IntPtr root = Functions.XRootWindow(display, screen);
Debug.Print("Display: {0}, Screen: {1}, RootWindow: {2}", display, screen, root);
unsafe
{
Debug.Print("Getting FB config.");
@ -190,81 +187,82 @@ namespace OpenTK.Platform.X11
if (fbcount > 0 && fbconfigs != null)
{
// We want to use the first GLXFBConfig from the fbconfigs array (the first one is the best match).
visual = Glx.GetVisualFromFBConfig(display, *fbconfigs);
Debug.Print("Selected FB config: {0}", *fbconfigs);
result = *fbconfigs;
Functions.XFree((IntPtr)fbconfigs);
}
else
{
Debug.Print("No matching FB config found.");
}
}
}
catch (EntryPointNotFoundException)
{
Debug.Print("Function glXChooseFBConfig not supported.");
return IntPtr.Zero;
}
}
return visual;
return result;
}
// See http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.opengl/doc/openglrf/glXChooseVisual.htm
IntPtr SelectVisualUsingChooseVisual(ColorFormat color, int depth, int stencil, int samples, ColorFormat accum,
int buffers, bool stereo)
IntPtr SelectVisual(GraphicsMode mode)
{
Debug.Print("Selecting FB config for {0}", mode);
List<int> visualAttributes = new List<int>();
Debug.Print("Bits per pixel: {0}", color.BitsPerPixel);
if (color.BitsPerPixel > 0)
if (mode.ColorFormat.BitsPerPixel > 0)
{
if (!color.IsIndexed)
if (!mode.ColorFormat.IsIndexed)
visualAttributes.Add((int)GLXAttribute.RGBA);
visualAttributes.Add((int)GLXAttribute.RED_SIZE);
visualAttributes.Add(color.Red);
visualAttributes.Add(mode.ColorFormat.Red);
visualAttributes.Add((int)GLXAttribute.GREEN_SIZE);
visualAttributes.Add(color.Green);
visualAttributes.Add(mode.ColorFormat.Green);
visualAttributes.Add((int)GLXAttribute.BLUE_SIZE);
visualAttributes.Add(color.Blue);
visualAttributes.Add(mode.ColorFormat.Blue);
visualAttributes.Add((int)GLXAttribute.ALPHA_SIZE);
visualAttributes.Add(color.Alpha);
visualAttributes.Add(mode.ColorFormat.Alpha);
}
Debug.Print("Depth: {0}", depth);
if (depth > 0)
if (mode.Depth > 0)
{
visualAttributes.Add((int)GLXAttribute.DEPTH_SIZE);
visualAttributes.Add(depth);
visualAttributes.Add(mode.Depth);
}
if (buffers > 1)
if (mode.Buffers > 1)
visualAttributes.Add((int)GLXAttribute.DOUBLEBUFFER);
if (stencil > 1)
if (mode.Stencil > 1)
{
visualAttributes.Add((int)GLXAttribute.STENCIL_SIZE);
visualAttributes.Add(stencil);
visualAttributes.Add(mode.Stencil);
}
if (accum.BitsPerPixel > 0)
if (mode.AccumulatorFormat.BitsPerPixel > 0)
{
visualAttributes.Add((int)GLXAttribute.ACCUM_ALPHA_SIZE);
visualAttributes.Add(accum.Alpha);
visualAttributes.Add(mode.AccumulatorFormat.Alpha);
visualAttributes.Add((int)GLXAttribute.ACCUM_BLUE_SIZE);
visualAttributes.Add(accum.Blue);
visualAttributes.Add(mode.AccumulatorFormat.Blue);
visualAttributes.Add((int)GLXAttribute.ACCUM_GREEN_SIZE);
visualAttributes.Add(accum.Green);
visualAttributes.Add(mode.AccumulatorFormat.Green);
visualAttributes.Add((int)GLXAttribute.ACCUM_RED_SIZE);
visualAttributes.Add(accum.Red);
visualAttributes.Add(mode.AccumulatorFormat.Red);
}
if (samples > 0)
if (mode.Samples > 0)
{
visualAttributes.Add((int)GLXAttribute.SAMPLE_BUFFERS);
visualAttributes.Add(1);
visualAttributes.Add((int)GLXAttribute.SAMPLES);
visualAttributes.Add(samples);
visualAttributes.Add(mode.Samples);
}
if (stereo)
if (mode.Stereo)
visualAttributes.Add((int)GLXAttribute.STEREO);
visualAttributes.Add(0);

View file

@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace OpenTK.Platform.X11
@ -87,7 +88,17 @@ namespace OpenTK.Platform.X11
/// <summary>Gets or sets the X11 screen.</summary>
public int Screen { get { return screen; } set { screen = value; } }
/// <summary>Gets or sets the X11 VisualInfo.</summary>
public XVisualInfo VisualInfo { get { return visualInfo; } set { visualInfo = value; } }
public XVisualInfo VisualInfo
{
get
{
if (Visual != IntPtr.Zero)
{
return (XVisualInfo)Marshal.PtrToStructure(Visual, typeof(XVisualInfo));
}
return default(XVisualInfo);
}
}
/// <summary>Gets or sets the X11 EventMask.</summary>
public EventMask EventMask { get { return eventMask; } set { eventMask = value; } }
@ -96,6 +107,10 @@ namespace OpenTK.Platform.X11
// (e.g. MonoGame)
public IntPtr WindowHandle { get { return Handle; } set { Handle = value; } }
public IntPtr Visual { get; set; }
public IntPtr FBConfig { get; set; }
public Graphics.GraphicsMode GraphicsMode { get; set; }
#endregion
#region --- IDisposable Members ---