diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj
index 794abfa5..4c2c827d 100644
--- a/Source/OpenTK/OpenTK.csproj
+++ b/Source/OpenTK/OpenTK.csproj
@@ -813,6 +813,8 @@
+
+
diff --git a/Source/OpenTK/Platform/Egl/EglContext.cs b/Source/OpenTK/Platform/Egl/EglContext.cs
index ee9d4939..575174ff 100644
--- a/Source/OpenTK/Platform/Egl/EglContext.cs
+++ b/Source/OpenTK/Platform/Egl/EglContext.cs
@@ -35,9 +35,9 @@ namespace OpenTK.Platform.Egl
{
#region Fields
- readonly RenderableFlags Renderable;
+ protected readonly RenderableFlags Renderable;
+ protected EglWindowInfo WindowInfo;
- EglWindowInfo WindowInfo;
IntPtr HandleAsEGLContext { get { return Handle.Handle; } set { Handle = new ContextHandle(value); } }
int swap_interval = 1; // Default interval is defined as 1 in EGL.
@@ -66,6 +66,14 @@ namespace OpenTK.Platform.Egl
{
Renderable = major > 1 ? RenderableFlags.ES2 : RenderableFlags.ES;
}
+
+ RenderApi api = (Renderable & RenderableFlags.GL) != 0 ? RenderApi.GL : RenderApi.ES;
+ Debug.Print("[EGL] Binding {0} rendering API.", api);
+ if (!Egl.BindAPI(api))
+ {
+ Debug.Print("[EGL] Failed to bind rendering API. Error: {0}", Egl.GetError());
+ }
+
Mode = new EglGraphicsMode().SelectGraphicsMode(window,
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo,
@@ -81,13 +89,6 @@ namespace OpenTK.Platform.Egl
HandleAsEGLContext = Egl.CreateContext(window.Display, config, shared != null ? shared.HandleAsEGLContext : IntPtr.Zero, attrib_list);
MakeCurrent(window);
-
- RenderApi api = (Renderable & RenderableFlags.GL) != 0 ? RenderApi.GL : RenderApi.ES;
- Debug.Print("[EGL] Binding rendering API {0}.", api);
- if (!Egl.BindAPI(api))
- {
- Debug.Print("[EGL] Failed to bind rendering API. Error: {0}", Egl.GetError());
- }
}
public EglContext(ContextHandle handle, EglWindowInfo window, IGraphicsContext sharedContext,
diff --git a/Source/OpenTK/Platform/Egl/EglUnixContext.cs b/Source/OpenTK/Platform/Egl/EglUnixContext.cs
index 5ceca2d1..94eb2e43 100644
--- a/Source/OpenTK/Platform/Egl/EglUnixContext.cs
+++ b/Source/OpenTK/Platform/Egl/EglUnixContext.cs
@@ -35,9 +35,9 @@ namespace OpenTK.Platform.Egl
{
class EglUnixContext : EglContext
{
- IntPtr GL = OpenTK.Platform.X11.DL.Open("libGL", X11.DLOpenFlags.Lazy);
IntPtr ES1 = OpenTK.Platform.X11.DL.Open("libGLESv1_CM", X11.DLOpenFlags.Lazy);
IntPtr ES2 = OpenTK.Platform.X11.DL.Open("libGLESv2", X11.DLOpenFlags.Lazy);
+ IntPtr GL = OpenTK.Platform.X11.DL.Open("libGL", X11.DLOpenFlags.Lazy);
public EglUnixContext(GraphicsMode mode, EglWindowInfo window, IGraphicsContext sharedContext,
int major, int minor, GraphicsContextFlags flags)
@@ -70,10 +70,6 @@ namespace OpenTK.Platform.Egl
protected override void Dispose(bool manual)
{
- if (GL != IntPtr.Zero)
- {
- X11.DL.Close(GL);
- }
if (ES1 != IntPtr.Zero)
{
X11.DL.Close(ES1);
@@ -82,6 +78,10 @@ namespace OpenTK.Platform.Egl
{
X11.DL.Close(ES2);
}
+ if (GL != IntPtr.Zero)
+ {
+ X11.DL.Close(GL);
+ }
GL = ES1 = ES2 = IntPtr.Zero;
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs
index 7f2bf590..06f43d61 100644
--- a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs
+++ b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs
@@ -32,12 +32,37 @@ using System.Runtime.InteropServices;
namespace OpenTK.Platform.Linux
{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate void VBlankCallback(int fd,
+ int sequence,
+ int tv_sec,
+ int tv_usec,
+ IntPtr user_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate void PageFlipCallback(int fd,
+ int sequence,
+ int tv_sec,
+ int tv_usec,
+ IntPtr user_data);
+
class Drm
{
const string lib = "libdrm";
+ [DllImport(lib, EntryPoint = "drmHandleEvent", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int HandleEvent(int fd, ref EventContext evctx);
+
+ [DllImport(lib, EntryPoint = "drmModeAddFB", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int ModeAddFB(int fd, int width, int height, byte depth,
+ byte bpp, int pitch, int bo_handle,
+ out int buf_id);
+
+ [DllImport(lib, EntryPoint = "drmModeRmFB", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int ModeRmFB(int fd, int bufferId);
+
[DllImport(lib, EntryPoint = "drmModeGetCrtc", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr ModeGetCrtc(int fd, uint crtcId);
+ public static extern IntPtr ModeGetCrtc(int fd, int crtcId);
[DllImport(lib, EntryPoint = "drmModeFreeConnector", CallingConvention = CallingConvention.Cdecl)]
public static extern void ModeFreeConnector(IntPtr ptr);
@@ -46,27 +71,31 @@ namespace OpenTK.Platform.Linux
public static extern void ModeFreeEncoder(IntPtr ptr);
[DllImport(lib, EntryPoint = "drmModeGetConnector", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr ModeGetConnector(int fd, uint connector_id);
+ public static extern IntPtr ModeGetConnector(int fd, int connector_id);
[DllImport(lib, EntryPoint = "drmModeGetEncoder", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr ModeGetEncoder(int fd, uint encoder_id);
+ public static extern IntPtr ModeGetEncoder(int fd, int encoder_id);
[DllImport(lib, EntryPoint = "drmModeGetResources", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ModeGetResources(int fd);
+ [DllImport(lib, EntryPoint = "drmModePageFlip", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int ModePageFlip(int fd, int crtc_id, int fb_id,
+ PageFlipFlags flags, IntPtr user_data);
+
[DllImport(lib, EntryPoint = "drmModeSetCrtc", CallingConvention = CallingConvention.Cdecl)]
- unsafe public static extern int ModeSetCrtc(int fd, uint crtcId, uint bufferId,
- uint x, uint y, uint* connectors, int count, ModeInfo* mode);
+ unsafe public static extern int ModeSetCrtc(int fd, int crtcId, int bufferId,
+ int x, int y, int* connectors, int count, ModeInfo* mode);
}
- enum ModeConnection : byte
+ enum ModeConnection
{
Connected = 1,
Disconnected = 2,
Unknown = 3
}
- enum ModeSubPixel : byte
+ enum ModeSubPixel
{
Unknown = 1,
HorizontalRgb = 2,
@@ -76,35 +105,53 @@ namespace OpenTK.Platform.Linux
None = 6
}
+ [Flags]
+ enum PageFlipFlags
+ {
+ FlipEvent = 0x01,
+ FlipAsync = 0x02,
+ FlipFlags = FlipEvent | FlipAsync
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct EventContext
+ {
+ public int version;
+ public IntPtr vblank_handler;
+ public IntPtr page_flip_handler;
+
+ public static readonly int Version = 2;
+ }
+
[StructLayout(LayoutKind.Sequential)]
unsafe struct ModeConnector
{
- public uint connector_id;
- public uint encoder_id;
- public uint connector_type;
- public uint connector_type_id;
+ public int connector_id;
+ public int encoder_id;
+ public int connector_type;
+ public int connector_type_id;
public ModeConnection connection;
- public uint mmWidth, mmHeight;
+ public int mmWidth, mmHeight;
public ModeSubPixel subpixel;
public int count_modes;
public ModeInfo* modes;
public int count_props;
- public uint *props;
- public ulong *prop_values;
+ public int *props;
+ public long *prop_values;
public int count_encoders;
- public uint *encoders;
+ public int *encoders;
}
struct ModeCrtc
{
- public uint crtc_id;
- public uint buffer_id;
+ public int crtc_id;
+ public int buffer_id;
- public uint x, y;
- public uint width, height;
+ public int x, y;
+ public int width, height;
public int mode_valid;
public ModeInfo mode;
@@ -113,11 +160,11 @@ namespace OpenTK.Platform.Linux
struct ModeEncoder
{
- public uint encoder_id;
- public uint encoder_type;
- public uint crtc_id;
- public uint possible_crtcs;
- public uint possible_clones;
+ public int encoder_id;
+ public int encoder_type;
+ public int crtc_id;
+ public int possible_crtcs;
+ public int possible_clones;
}
[StructLayout(LayoutKind.Sequential)]
@@ -127,7 +174,7 @@ namespace OpenTK.Platform.Linux
public ushort hdisplay, hsync_start, hsync_end, htotal, hskew;
public ushort vdisplay, vsync_start, vsync_end, vtotal, vscan;
- public uint vrefresh; // refresh rate * 1000
+ public int vrefresh; // refresh rate * 1000
public uint flags;
public uint type;
@@ -138,13 +185,13 @@ namespace OpenTK.Platform.Linux
unsafe struct ModeRes
{
public int count_fbs;
- public uint* fbs;
+ public int* fbs;
public int count_crtcs;
- public uint* crtcs;
+ public int* crtcs;
public int count_connectors;
- public uint* connectors;
+ public int* connectors;
public int count_encoders;
- public uint* encoders;
+ public int* encoders;
public int min_width, max_width;
public int min_height, max_height;
}
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
index 9225e442..4c11eda6 100644
--- a/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
+++ b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
@@ -32,24 +32,57 @@ using System.Runtime.InteropServices;
namespace OpenTK.Platform.Linux
{
- using GbmDevice = IntPtr; // opaque pointer "struct gbm_device*"
+ using Device = IntPtr; // opaque pointer "struct gbm_device*"
+ using Surface = IntPtr;
+ using BufferObject = IntPtr;
+ using BufferObjectHandle = IntPtr;
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate void DestroyUserDataCallback(BufferObject bo, IntPtr data);
class Gbm
{
const string lib = "gbm";
+ [DllImport(lib, EntryPoint = "gbm_bo_get_device", CallingConvention = CallingConvention.Cdecl)]
+ public static extern Device BOGetDevice(BufferObject bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_handle", CallingConvention = CallingConvention.Cdecl)]
+ public static extern BufferObjectHandle BOGetHandle(BufferObject bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_height", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int BOGetHeight(BufferObject bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_width", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int BOGetWidth(BufferObject bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_stride", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int BOGetStride(BufferObject bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_set_user_data", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void BOSetUserData(BufferObject bo, IntPtr data, DestroyUserDataCallback callback);
+
[DllImport(lib, EntryPoint = "gbm_create_device", CallingConvention = CallingConvention.Cdecl)]
- public static extern GbmDevice CreateDevice(int fd);
+ public static extern Device CreateDevice(int fd);
+
+ [DllImport(lib, EntryPoint = "gbm_device_get_fd", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int DeviceGetFD(IntPtr gbm);
[DllImport(lib, EntryPoint = "gbm_surface_create", CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr CreateSurface(GbmDevice gbm, int width, int height, SurfaceFormat format, SurfaceFlags flags);
+ public static extern Surface CreateSurface(Device gbm, int width, int height, SurfaceFormat format, SurfaceFlags flags);
[DllImport(lib, EntryPoint = "gbm_surface_destroy", CallingConvention = CallingConvention.Cdecl)]
public static extern void DestroySurface(IntPtr surface);
[DllImport(lib, EntryPoint = "gbm_device_is_format_supported", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool IsFormatSupported(GbmDevice gbm, SurfaceFormat format, SurfaceFlags usage);
+ public static extern bool IsFormatSupported(Device gbm, SurfaceFormat format, SurfaceFlags usage);
+
+ [DllImport(lib, EntryPoint = "gbm_surface_lock_front_buffer", CallingConvention = CallingConvention.Cdecl)]
+ public static extern BufferObject LockFrontBuffer(Surface surface);
+
+ [DllImport(lib, EntryPoint = "gbm_surface_release_buffer", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void ReleaseBuffer(Surface surface, BufferObject buffer);
}
enum SurfaceFormat
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Libc.cs b/Source/OpenTK/Platform/Linux/Bindings/Libc.cs
index c571e6d8..a4df9710 100644
--- a/Source/OpenTK/Platform/Linux/Bindings/Libc.cs
+++ b/Source/OpenTK/Platform/Linux/Bindings/Libc.cs
@@ -33,7 +33,7 @@ using System.Text;
namespace OpenTK.Platform.Linux
{
- class Libc
+ partial class Libc
{
const string lib = "libc";
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Poll.cs b/Source/OpenTK/Platform/Linux/Bindings/Poll.cs
new file mode 100644
index 00000000..67963695
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Poll.cs
@@ -0,0 +1,65 @@
+#region License
+//
+// Poll.cs
+//
+// Author:
+// Stefanos A.
+//
+// Copyright (c) 2006-2014 Stefanos Apostolopoulos
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ partial class Libc
+ {
+ [DllImport(lib)]
+ public static extern int poll(ref PollFD fd, IntPtr fd_count, int timeout);
+
+ public static int poll(ref PollFD fd, int fd_count, int timeout)
+ {
+ return poll(ref fd, (IntPtr)fd_count, timeout);
+ }
+ }
+
+ [Flags]
+ enum PollFlags : short
+ {
+ In = 0x01,
+ Pri = 0x02,
+ Out = 0x04,
+ Error = 0x08,
+ Hup = 0x10,
+ Nval = 0x20,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct PollFD
+ {
+ public int fd;
+ public PollFlags events;
+ public PollFlags revents;
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
index a12fd06e..14f6fb82 100644
--- a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
+++ b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
@@ -35,53 +35,79 @@ using OpenTK.Graphics;
namespace OpenTK.Platform.Linux
{
- class LinuxDisplayDriver : DisplayDeviceBase
+ // Stores platform-specific information about a display
+ class LinuxDisplay
{
- unsafe class LinuxDisplay
+ public IntPtr Connector;
+ public IntPtr Crtc;
+ public IntPtr Encoder;
+
+ unsafe public ModeConnector* pConnector { get { return (ModeConnector*)Connector; } }
+ unsafe public ModeCrtc* pCrtc { get { return (ModeCrtc*)Crtc; } }
+ unsafe public ModeEncoder* pEncoder { get { return (ModeEncoder*)Encoder; } }
+ /*
+ public ModeInfo Mode
{
- public ModeConnector* Connector;
- public ModeEncoder* Encoder;
- public ModeCrtc* Crtc;
- public ModeInfo Mode
+ get
{
- get
- {
- if (Crtc == null)
- throw new InvalidOperationException();
+ if (Crtc == IntPtr.Zero)
+ throw new InvalidOperationException();
- return Crtc->mode;
+ unsafe
+ {
+ return pCrtc->mode;
}
}
- public ModeInfo OriginalMode;
+ }
+ */
- public int Id
+ public ModeInfo OriginalMode;
+
+ public int Id
+ {
+ get
{
- get
+ if (Crtc == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ unsafe
{
- if (Crtc == null)
- throw new InvalidOperationException();
-
- return (int)Crtc->crtc_id;
+ return (int)pCrtc->crtc_id;
}
}
-
- public LinuxDisplay(ModeConnector* c, ModeEncoder* e, ModeCrtc* r)
- {
- Connector = c;
- Encoder = e;
- Crtc = r;
- OriginalMode = Crtc->mode; // in case we change resolution later on
- }
}
+ public LinuxDisplay(IntPtr c, IntPtr e, IntPtr r)
+ {
+ Connector = c;
+ Encoder = e;
+ Crtc = r;
+ unsafe
+ {
+ OriginalMode = pCrtc->mode; // in case we change resolution later on
+ }
+ }
+ }
+
+ class LinuxDisplayDriver : DisplayDeviceBase
+ {
readonly int FD;
readonly Dictionary DisplayIds =
new Dictionary();
public LinuxDisplayDriver(int fd)
{
- FD = fd;
- QueryDisplays();
+ Debug.Print("[KMS] Creating LinuxDisplayDriver for fd:{0}", fd);
+ Debug.Indent();
+ try
+ {
+ FD = fd;
+ QueryDisplays();
+ }
+ finally
+ {
+ Debug.Unindent();
+ }
}
void QueryDisplays()
@@ -109,7 +135,7 @@ namespace OpenTK.Platform.Linux
if (connector != null)
{
if (connector->connection == ModeConnection.Connected &&
- connector->count_modes > 0)
+ connector->count_modes > 0)
{
// Connector found!
AddDisplay(connector);
@@ -176,20 +202,28 @@ namespace OpenTK.Platform.Linux
return;
}
- LinuxDisplay display = new LinuxDisplay(c, encoder, crtc);
+ LinuxDisplay display = new LinuxDisplay((IntPtr)c, (IntPtr)encoder, (IntPtr)crtc);
if (!DisplayIds.ContainsKey(display.Id))
{
+ Debug.Print("[KMS] Adding display {0} as {1}", display.Id, AvailableDevices.Count);
DisplayIds.Add(display.Id, AvailableDevices.Count);
}
- List modes = new List();
- for (int i = 0; i < display.Connector->count_modes; i++)
+ int mode_count = display.pConnector->count_modes;
+ Debug.Print("[KMS] Display supports {0} modes", mode_count);
+ List modes = new List(mode_count);
+ for (int i = 0; i < mode_count; i++)
{
- ModeInfo* mode = display.Connector->modes + i;
- DisplayResolution res = GetDisplayResolution(mode);
- modes.Add(res);
+ ModeInfo* mode = display.pConnector->modes + i;
+ if (mode != null)
+ {
+ Debug.Print("Mode {0}: {1}x{2} @{3}", i,
+ mode->hdisplay, mode->vdisplay, mode->vrefresh);
+ DisplayResolution res = GetDisplayResolution(mode);
+ modes.Add(res);
+ }
}
- ModeInfo current_mode = display.Mode;
+ ModeInfo current_mode = display.pCrtc->mode;
DisplayResolution current = GetDisplayResolution(¤t_mode);
// Note: since we are not running a display manager, we are free
@@ -211,30 +245,27 @@ namespace OpenTK.Platform.Linux
Primary = device;
}
- Debug.Print("[KMS] Detected display {0}", device);
+ Debug.Print("[KMS] Added DisplayDevice {0}", device);
AvailableDevices.Add(device);
}
unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode)
{
- if (mode == null)
- throw new ArgumentNullException();
-
return new DisplayResolution(
0, 0,
- mode->htotal, mode->vtotal,
+ mode->hdisplay, mode->vdisplay,
32, // This is actually part of the framebuffer, not the DisplayResolution
- mode->vrefresh / 1000.0f);
+ mode->vrefresh);
}
unsafe static ModeInfo* GetModeInfo(LinuxDisplay display, DisplayResolution resolution)
{
- for (int i = 0; i < display.Connector->count_modes; i++)
+ for (int i = 0; i < display.pConnector->count_modes; i++)
{
- ModeInfo* mode = display.Connector->modes + i;
+ ModeInfo* mode = display.pConnector->modes + i;
if (mode != null &&
- mode->htotal == resolution.Width &&
- mode->vtotal == resolution.Height)
+ mode->hdisplay == resolution.Width &&
+ mode->vdisplay == resolution.Height)
{
return mode;
}
@@ -250,10 +281,10 @@ namespace OpenTK.Platform.Linux
{
LinuxDisplay display = (LinuxDisplay)device.Id;
ModeInfo* mode = GetModeInfo(display, resolution);
- uint connector_id = display.Connector->connector_id;
+ int connector_id = display.pConnector->connector_id;
if (mode != null)
{
- return Drm.ModeSetCrtc(FD, (uint)display.Id, 0, 0, 0,
+ return Drm.ModeSetCrtc(FD, display.Id, 0, 0, 0,
&connector_id, 1, mode) == 0;
}
return false;
@@ -265,9 +296,9 @@ namespace OpenTK.Platform.Linux
unsafe
{
LinuxDisplay display = (LinuxDisplay)device.Id;
- uint connector_id = display.Connector->connector_id;
ModeInfo mode = display.OriginalMode;
- return Drm.ModeSetCrtc(FD, (uint)display.Id, 0, 0, 0,
+ int connector_id = display.pConnector->connector_id;
+ return Drm.ModeSetCrtc(FD, display.Id, 0, 0, 0,
&connector_id, 1, &mode) == 0;
}
}
diff --git a/Source/OpenTK/Platform/Linux/LinuxFactory.cs b/Source/OpenTK/Platform/Linux/LinuxFactory.cs
index 30661205..11e5e80b 100644
--- a/Source/OpenTK/Platform/Linux/LinuxFactory.cs
+++ b/Source/OpenTK/Platform/Linux/LinuxFactory.cs
@@ -29,6 +29,7 @@
using System;
using System.Diagnostics;
+using System.IO;
using System.Runtime.InteropServices;
using OpenTK.Graphics;
using OpenTK.Input;
@@ -48,21 +49,62 @@ namespace OpenTK.Platform.Linux
IJoystickDriver2 JoystickDriver;
IDisplayDeviceDriver DisplayDriver;
+ const string gpu_path = "/dev/dri"; // card0, card1, ...
+
public LinuxFactory()
{
- SetupEgl();
+ // Query all GPUs until we find one that has a connected display.
+ // This is necessary in multi-gpu systems, where only one GPU
+ // can output a signal.
+ // Todo: allow OpenTK to drive multiple GPUs
+ // Todo: allow OpenTK to run on an offscreen GPU
+ // Todo: allow the user to pick a GPU
+ var files = Directory.GetFiles(gpu_path);
+ foreach (var gpu in files)
+ {
+ if (Path.GetFileName(gpu).StartsWith("card"))
+ {
+ int test_fd = SetupDisplay(gpu);
+ if (test_fd >= 0)
+ {
+ try
+ {
+ DisplayDriver = new LinuxDisplayDriver(test_fd);
+ if (DisplayDriver.GetDisplay(DisplayIndex.Primary) != null)
+ {
+ fd = test_fd;
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ }
+
+ Debug.Print("[KMS] GPU '{0}' is not connected, skipping.", gpu);
+ Libc.close(test_fd);
+ }
+ }
+ }
+
+ if (fd == 0)
+ {
+ Debug.Print("[Error] No valid GPU found, bailing out.");
+ throw new PlatformNotSupportedException();
+ }
}
#region Private Members
- void SetupEgl()
+ int SetupDisplay(string gpu)
{
- // Todo: support multi-GPU systems
- string gpu = "/dev/dri/card0";
- fd = Libc.open(gpu, OpenFlags.ReadWrite | OpenFlags.CloseOnExec);
+ Debug.Print("[KMS] Attempting to use gpu '{0}'.", gpu);
+
+ int fd = Libc.open(gpu, OpenFlags.ReadWrite | OpenFlags.CloseOnExec);
if (fd < 0)
{
- throw new NotSupportedException("[KMS] No KMS-capable GPU available");
+ Debug.Print("[KMS] Failed to open gpu");
+ return fd;
}
Debug.Print("[KMS] GPU '{0}' opened as fd:{1}", gpu, fd);
@@ -87,6 +129,8 @@ namespace OpenTK.Platform.Linux
throw new NotSupportedException("[KMS] Failed to initialize EGL display. Error code: " + error);
}
Debug.Print("[KMS] EGL {0}.{1} initialized successfully on display {2:x}", major, minor, display);
+
+ return fd;
}
#endregion
@@ -95,21 +139,17 @@ namespace OpenTK.Platform.Linux
public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice display_device)
{
- return new LinuxNativeWindow(display, gbm_device, x, y, width, height, title, mode, options, display_device);
+ return new LinuxNativeWindow(display, gbm_device, fd, x, y, width, height, title, mode, options, display_device);
}
public override IDisplayDeviceDriver CreateDisplayDeviceDriver()
{
- lock (this)
- {
- DisplayDriver = DisplayDriver ?? new LinuxDisplayDriver(fd);
- return DisplayDriver;
- }
+ return DisplayDriver;
}
public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags)
{
- return new EglUnixContext(mode, (EglWindowInfo)window, shareContext, major, minor, flags);
+ return new LinuxGraphicsContext(mode, (LinuxWindowInfo)window, shareContext, major, minor, flags);
}
public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext()
diff --git a/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
new file mode 100644
index 00000000..f42d1708
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
@@ -0,0 +1,297 @@
+#region License
+//
+// LinuxGraphicsContext.cs
+//
+// Author:
+// thefiddler
+//
+// Copyright (c) 2006-2014
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using OpenTK.Graphics;
+
+namespace OpenTK.Platform.Linux
+{
+ /// \internal
+ ///
+ /// Defines an IGraphicsContext implementation for the Linux KMS framebuffer.
+ /// For Linux/X11 and other Unix operating systems, use the more generic
+ /// instead.
+ ///
+ ///
+ /// Note: to display our results, we need to allocate a GBM framebuffer
+ /// and point the scanout address to that via Drm.ModeSetCrtc.
+ ///
+ class LinuxGraphicsContext : Egl.EglUnixContext
+ {
+ IntPtr bo, bo_next;
+ int fd;
+ bool is_flip_queued;
+ int swap_interval;
+
+ public LinuxGraphicsContext(GraphicsMode mode, LinuxWindowInfo window, IGraphicsContext sharedContext,
+ int major, int minor, GraphicsContextFlags flags)
+ : base(mode, window, sharedContext, major, minor, flags)
+ {
+ if (mode.Buffers < 1)
+ throw new ArgumentException();
+ fd = window.FD;
+
+ PageFlip = HandlePageFlip;
+ PageFlipPtr = Marshal.GetFunctionPointerForDelegate(PageFlip);
+ }
+
+ public override void SwapBuffers()
+ {
+ base.SwapBuffers();
+
+ bo_next = LockSurface();
+ int fb = GetFramebuffer(bo_next);
+
+ if (is_flip_queued)
+ {
+ WaitFlip(SwapInterval > 0);
+ if (is_flip_queued)
+ {
+ Debug.Print("[KMS] Dropping frame");
+ return;
+ }
+ }
+
+ QueueFlip(fb);
+ }
+
+ public override void Update(IWindowInfo window)
+ {
+ WaitFlip(true);
+
+ base.SwapBuffers();
+
+ bo = LockSurface();
+ int fb = GetFramebuffer(bo);
+ SetScanoutRegion(fb);
+ }
+
+ public override int SwapInterval
+ {
+ get
+ {
+ return swap_interval;
+ }
+ set
+ {
+ // We only support a SwapInterval of 0 (immediate)
+ // or 1 (vsynced).
+ // Todo: add support for SwapInterval of -1 (adaptive).
+ // This requires a small change in WaitFlip().
+ swap_interval = MathHelper.Clamp(value, 0, 1);
+ }
+ }
+
+ void WaitFlip(bool block)
+ {
+ PollFD fds = new PollFD();
+ fds.fd = fd;
+ fds.events = PollFlags.In;
+
+ EventContext evctx = new EventContext();
+ evctx.version = EventContext.Version;
+ evctx.page_flip_handler = PageFlipPtr;
+
+ int timeout = block ? -1 : 0;
+
+ while (is_flip_queued)
+ {
+ fds.revents = 0;
+ if (Libc.poll(ref fds, 1, timeout) < 0)
+ break;
+
+ if ((fds.revents & (PollFlags.Hup | PollFlags.Error)) != 0)
+ break;
+
+ if ((fds.revents & PollFlags.In) != 0)
+ Drm.HandleEvent(fd, ref evctx);
+ else
+ break;
+ }
+
+ // Page flip has taken place, update buffer objects
+ if (!is_flip_queued)
+ {
+ IntPtr gbm_surface = WindowInfo.Handle;
+ Gbm.ReleaseBuffer(gbm_surface, bo);
+ bo = bo_next;
+ }
+ }
+
+ void QueueFlip(int buffer)
+ {
+ LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo;
+ if (wnd == null)
+ throw new InvalidOperationException();
+
+ unsafe
+ {
+ int ret = Drm.ModePageFlip(fd, wnd.DisplayDevice.Id, buffer,
+ PageFlipFlags.FlipEvent, IntPtr.Zero);
+
+ if (ret < 0)
+ {
+ Debug.Print("[KMS] Failed to enqueue framebuffer flip. Error: {0}", ret);
+ }
+
+ is_flip_queued = true;
+ }
+ }
+
+ void SetScanoutRegion(int buffer)
+ {
+ LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo;
+ if (wnd == null)
+ throw new InvalidOperationException();
+
+ unsafe
+ {
+ ModeInfo* mode = wnd.DisplayDevice.pConnector->modes;
+ int connector_id = wnd.DisplayDevice.pConnector->connector_id;
+ int crtc_id = wnd.DisplayDevice.Id;
+
+ int x = 0;
+ int y = 0;
+ int connector_count = 1;
+ int ret = Drm.ModeSetCrtc(fd, crtc_id, buffer, x, y,
+ &connector_id, connector_count, mode);
+
+ if (ret != 0)
+ {
+ Debug.Print("[KMS] Drm.ModeSetCrtc{0}, {1}, {2}, {3}, {4:x}, {5}, {6:x}) failed. Error: {7}",
+ fd, crtc_id, buffer, x, y, (IntPtr)connector_id, connector_count, (IntPtr)mode, ret);
+ }
+ }
+ }
+
+ IntPtr LockSurface()
+ {
+ IntPtr gbm_surface = WindowInfo.Handle;
+ return Gbm.LockFrontBuffer(gbm_surface);
+ }
+
+ int GetFramebuffer(IntPtr bo)
+ {
+ if (bo == IntPtr.Zero)
+ goto fail;
+
+ int bo_handle = Gbm.BOGetHandle(bo).ToInt32();
+ if (bo_handle == 0)
+ {
+ Debug.Print("[KMS] Gbm.BOGetHandle({0:x}) failed.", bo);
+ goto fail;
+ }
+
+ int width = Gbm.BOGetWidth(bo);
+ int height = Gbm.BOGetHeight(bo);
+ int bpp = Mode.ColorFormat.BitsPerPixel;
+ int depth = Mode.Depth;
+ int stride = Gbm.BOGetStride(bo);
+
+ if (width == 0 || height == 0 || bpp == 0)
+ {
+ Debug.Print("[KMS] Invalid framebuffer format: {0}x{1} {2} {3} {4}",
+ width, height, stride, bpp, depth);
+ goto fail;
+ }
+
+ int buffer;
+ int ret = Drm.ModeAddFB(
+ fd, width, height,
+ (byte)depth, (byte)bpp, stride, bo_handle,
+ out buffer);
+ if (ret != 0)
+ {
+ Debug.Print("[KMS] Drm.ModeAddFB({0}, {1}, {2}, {3}, {4}, {5}, {6}) failed. Error: {7}",
+ fd, width, height, depth, bpp, stride, bo_handle, ret);
+ goto fail;
+ }
+
+ Gbm.BOSetUserData(bo, (IntPtr)buffer, DestroyFB);
+ return buffer;
+
+ fail:
+ Debug.Print("[Error] Failed to create framebuffer.");
+ return -1;
+ }
+
+ readonly IntPtr PageFlipPtr;
+ readonly PageFlipCallback PageFlip;
+ void HandlePageFlip(int fd,
+ int sequence,
+ int tv_sec,
+ int tv_usec,
+ IntPtr user_data)
+ {
+ is_flip_queued = false;
+ }
+
+ static readonly DestroyUserDataCallback DestroyFB = HandleDestroyFB;
+ static void HandleDestroyFB(IntPtr bo, IntPtr data)
+ {
+ IntPtr gbm = Gbm.BOGetDevice(bo);
+ int fb = data.ToInt32();
+ Debug.Print("[KMS] Destroying framebuffer {0}", fb);
+
+ if (fb != 0)
+ {
+ Drm.ModeRmFB(Gbm.DeviceGetFD(gbm), fb);
+ }
+ }
+
+ protected override void Dispose(bool manual)
+ {
+ if (manual)
+ {
+ // Reset the scanout region
+ LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo;
+ if (wnd != null)
+ {
+ unsafe
+ {
+ int connector_id = wnd.DisplayDevice.pConnector->connector_id;
+ ModeInfo mode = wnd.DisplayDevice.OriginalMode;
+ Drm.ModeSetCrtc(fd,
+ wnd.DisplayDevice.pCrtc->crtc_id,
+ wnd.DisplayDevice.pCrtc->buffer_id,
+ wnd.DisplayDevice.pCrtc->width,
+ wnd.DisplayDevice.pCrtc->height,
+ &connector_id,
+ 1,
+ &mode);
+ }
+ }
+ }
+ base.Dispose(manual);
+ }
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs b/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs
index ee3bf89a..16ae7716 100644
--- a/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs
+++ b/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs
@@ -47,7 +47,7 @@ namespace OpenTK.Platform.Linux
Rectangle bounds;
Size client_size;
- public LinuxNativeWindow(IntPtr display, IntPtr gbm,
+ public LinuxNativeWindow(IntPtr display, IntPtr gbm, int fd,
int x, int y, int width, int height, string title,
GraphicsMode mode, GameWindowFlags options,
DisplayDevice display_device)
@@ -58,7 +58,8 @@ namespace OpenTK.Platform.Linux
bounds = new Rectangle(0, 0, width, height);
client_size = bounds.Size;
- window = new LinuxWindowInfo(display);
+ window = new LinuxWindowInfo(display, fd, display_device.Id as LinuxDisplay);
+
if (!mode.Index.HasValue)
{
mode = new EglGraphicsMode().SelectGraphicsMode(window, mode, 0);
@@ -69,7 +70,17 @@ namespace OpenTK.Platform.Linux
SurfaceFlags usage = SurfaceFlags.Rendering | SurfaceFlags.Scanout;
if (!Gbm.IsFormatSupported(gbm, format, usage))
{
- //format = SurfaceFormat.xrgba;
+ Debug.Print("[KMS] Failed to find suitable surface format, using XRGB8888");
+ format = SurfaceFormat.XRGB8888;
+ }
+
+ // Note: we only support fullscreen windows on KMS.
+ // We implicitly override the requested width and height
+ // by the width and height of the DisplayDevice, if any.
+ if (display_device != null)
+ {
+ width = display_device.Width;
+ height = display_device.Height;
}
Debug.Print("[KMS] Creating GBM surface on {0:x} with {1}x{2} {3} [{4}]",
diff --git a/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs b/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs
index 0a1f0933..b6df115a 100644
--- a/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs
+++ b/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs
@@ -28,15 +28,24 @@
#endregion
using System;
+using System.Diagnostics;
using OpenTK.Platform.Egl;
namespace OpenTK.Platform.Linux
{
class LinuxWindowInfo : EglWindowInfo
{
- public LinuxWindowInfo(IntPtr display)
+ public int FD { get; private set; }
+ public LinuxDisplay DisplayDevice { get; private set; }
+
+ public LinuxWindowInfo(IntPtr display, int fd, LinuxDisplay display_device)
: base(IntPtr.Zero, display, IntPtr.Zero)
{
+ if (display_device == null)
+ throw new ArgumentNullException();
+
+ FD = fd;
+ DisplayDevice = display_device;
// The window handle and surface handle must
// be filled in manually once they are known.
}