diff --git a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs index 06f43d61..c0704837 100644 --- a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs +++ b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs @@ -61,6 +61,9 @@ namespace OpenTK.Platform.Linux [DllImport(lib, EntryPoint = "drmModeRmFB", CallingConvention = CallingConvention.Cdecl)] public static extern int ModeRmFB(int fd, int bufferId); + [DllImport(lib, EntryPoint = "drmModeFreeCrtc", CallingConvention = CallingConvention.Cdecl)] + public static extern void ModeFreeCrtc(IntPtr ptr); + [DllImport(lib, EntryPoint = "drmModeGetCrtc", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr ModeGetCrtc(int fd, int crtcId); diff --git a/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs index 4c11eda6..05e1ffd9 100644 --- a/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs +++ b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs @@ -65,6 +65,9 @@ namespace OpenTK.Platform.Linux [DllImport(lib, EntryPoint = "gbm_create_device", CallingConvention = CallingConvention.Cdecl)] public static extern Device CreateDevice(int fd); + [DllImport(lib, EntryPoint = "gbm_destroy_device", CallingConvention = CallingConvention.Cdecl)] + public static extern void DestroyDevice(Device gbm); + [DllImport(lib, EntryPoint = "gbm_device_get_fd", CallingConvention = CallingConvention.Cdecl)] public static extern int DeviceGetFD(IntPtr gbm); diff --git a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs index 14f6fb82..ad38c523 100644 --- a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs +++ b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs @@ -102,7 +102,7 @@ namespace OpenTK.Platform.Linux try { FD = fd; - QueryDisplays(); + UpdateDisplays(fd); } finally { @@ -110,7 +110,76 @@ namespace OpenTK.Platform.Linux } } - void QueryDisplays() + /// \internal + /// + /// Queries the specified GPU for connected displays and, optionally, + /// returns the list of displays. + /// + /// true, if at least one display is connected, false otherwise. + /// The fd for the GPU to query, obtained through open("/dev/dri/card0"). + /// + /// If not null, this will contain a list instances, + /// one for each connected display. + /// + internal static bool QueryDisplays(int fd, List displays) + { + unsafe + { + bool has_displays = false; + if (displays != null) + { + displays.Clear(); + } + + ModeRes* resources = (ModeRes*)Drm.ModeGetResources(fd); + if (resources == null) + { + Debug.Print("[KMS] Drm.ModeGetResources failed."); + return false; + } + Debug.Print("[KMS] DRM found {0} connectors", resources->count_connectors); + + // Search for a valid connector + ModeConnector* connector = null; + for (int i = 0; i < resources->count_connectors; i++) + { + connector = (ModeConnector*)Drm.ModeGetConnector(fd, + *(resources->connectors + i)); + if (connector != null) + { + bool success = false; + LinuxDisplay display = null; + try + { + if (connector->connection == ModeConnection.Connected && + connector->count_modes > 0) + { + success = QueryDisplay(fd, connector, out display); + has_displays |= success; + } + } + catch (Exception e) + { + Debug.Print("[KMS] Failed to add display. Error: {0}", e); + } + + if (success && displays != null) + { + displays.Add(display); + } + else + { + Drm.ModeFreeConnector((IntPtr)connector); + connector = null; + } + } + } + + return has_displays; + } + } + + void UpdateDisplays(int fd) { unsafe { @@ -119,33 +188,12 @@ namespace OpenTK.Platform.Linux AvailableDevices.Clear(); DisplayIds.Clear(); - ModeRes* resources = (ModeRes*)Drm.ModeGetResources(FD); - if (resources == null) + List displays = new List(); + if (QueryDisplays(fd, displays)) { - throw new NotSupportedException("[KMS] DRM ModeGetResources failed"); - } - Debug.Print("[KMS] DRM found {0} connectors", resources->count_connectors); - - // Search for a valid connector - ModeConnector* connector = null; - for (int i = 0; i < resources->count_connectors; i++) - { - connector = (ModeConnector*)Drm.ModeGetConnector(FD, - *(resources->connectors + i)); - if (connector != null) + foreach (LinuxDisplay display in displays) { - if (connector->connection == ModeConnection.Connected && - connector->count_modes > 0) - { - // Connector found! - AddDisplay(connector); - } - else - { - // This is not the display we are looking for - Drm.ModeFreeConnector((IntPtr)connector); - connector = null; - } + AddDisplay(display); } } @@ -157,14 +205,13 @@ namespace OpenTK.Platform.Linux } } - unsafe void AddDisplay(ModeConnector* c) + unsafe static ModeEncoder* GetEncoder(int fd, ModeConnector* c) { - // Find corresponding encoder ModeEncoder* encoder = null; for (int i = 0; i < c->count_encoders && encoder == null; i++) { ModeEncoder* e = (ModeEncoder*)Drm.ModeGetEncoder( - FD, *(c->encoders + i)); + fd, *(c->encoders + i)); if (e != null) { if (e->encoder_id == c->encoder_id) @@ -186,10 +233,14 @@ namespace OpenTK.Platform.Linux else { Debug.Print("[KMS] Failed to find encoder for connector {0}", c->connector_id); - return; } - ModeCrtc* crtc = (ModeCrtc*)Drm.ModeGetCrtc(FD, encoder->crtc_id); + return encoder; + } + + unsafe static ModeCrtc* GetCrtc(int fd, ModeEncoder* encoder) + { + ModeCrtc* crtc = (ModeCrtc*)Drm.ModeGetCrtc(fd, encoder->crtc_id); if (crtc != null) { Debug.Print("[KMS] CRTC {0} found for encoder {1}", @@ -199,19 +250,14 @@ namespace OpenTK.Platform.Linux { Debug.Print("[KMS] Failed to find crtc {0} for encoder {1}", encoder->crtc_id, encoder->encoder_id); - return; - } - - 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); } + return crtc; + } + unsafe static void GetModes(LinuxDisplay display, DisplayResolution[] modes, out DisplayResolution current) + { 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.pConnector->modes + i; @@ -220,33 +266,89 @@ namespace OpenTK.Platform.Linux Debug.Print("Mode {0}: {1}x{2} @{3}", i, mode->hdisplay, mode->vdisplay, mode->vrefresh); DisplayResolution res = GetDisplayResolution(mode); - modes.Add(res); + modes[i] = res; } } - ModeInfo current_mode = display.pCrtc->mode; - DisplayResolution current = GetDisplayResolution(¤t_mode); + if (display.pCrtc->mode_valid != 0) + { + ModeInfo cmode = display.pCrtc->mode; + current = GetDisplayResolution(&cmode); + } + else + { + current = GetDisplayResolution(display.pConnector->modes); + } + Debug.Print("Current mode: {0}", current.ToString()); + } + + System.Drawing.Rectangle GetBounds(DisplayResolution current) + { // Note: since we are not running a display manager, we are free // to choose the display layout for multiple displays ourselves. // We choose the simplest layout: displays are laid out side-by-side // from left to right. Primary display is the first display we encounter. - System.Drawing.Rectangle bounds = - new System.Drawing.Rectangle( - AvailableDevices.Count == 0 ? 0 : AvailableDevices[0].Bounds.Right, - 0, - current.Width, - current.Height); + int x = AvailableDevices.Count == 0 ? + 0 : AvailableDevices[AvailableDevices.Count - 1].Bounds.Right; + int y = 0; + + return new System.Drawing.Rectangle( + x, y, current.Width, current.Height); + } + + void UpdateDisplayIndices(LinuxDisplay display, DisplayDevice device) + { + if (!DisplayIds.ContainsKey(display.Id)) + { + Debug.Print("[KMS] Adding display {0} as {1}", display.Id, AvailableDevices.Count); + DisplayIds.Add(display.Id, AvailableDevices.Count); + } + int index = DisplayIds[display.Id]; + if (index >= AvailableDevices.Count) + { + AvailableDevices.Add(device); + } + else + { + AvailableDevices[index] = device; + } + } + + unsafe static bool QueryDisplay(int fd, ModeConnector* c, out LinuxDisplay display) + { + display = null; + + // Find corresponding encoder + ModeEncoder* encoder = GetEncoder(fd, c); + if (encoder == null) + return false; + + ModeCrtc* crtc = GetCrtc(fd, encoder); + if (crtc == null) + return false; + + display = new LinuxDisplay((IntPtr)c, (IntPtr)encoder, (IntPtr)crtc); + return true; + } + + unsafe void AddDisplay(LinuxDisplay display) + { + DisplayResolution[] modes = new DisplayResolution[display.pConnector->count_modes]; + DisplayResolution current; + GetModes(display, modes, out current); + bool is_primary = AvailableDevices.Count == 0; DisplayDevice device = new DisplayDevice(current, is_primary, - modes, bounds, display); + modes, GetBounds(current), display); - if (AvailableDevices.Count == 0) + if (is_primary) { Primary = device; } + UpdateDisplayIndices(display, device); + Debug.Print("[KMS] Added DisplayDevice {0}", device); - AvailableDevices.Add(device); } unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode) diff --git a/Source/OpenTK/Platform/Linux/LinuxFactory.cs b/Source/OpenTK/Platform/Linux/LinuxFactory.cs index 11e5e80b..45315d85 100644 --- a/Source/OpenTK/Platform/Linux/LinuxFactory.cs +++ b/Source/OpenTK/Platform/Linux/LinuxFactory.cs @@ -69,8 +69,7 @@ namespace OpenTK.Platform.Linux { try { - DisplayDriver = new LinuxDisplayDriver(test_fd); - if (DisplayDriver.GetDisplay(DisplayIndex.Primary) != null) + if (LinuxDisplayDriver.QueryDisplays(test_fd, null)) { fd = test_fd; break; @@ -135,6 +134,30 @@ namespace OpenTK.Platform.Linux #endregion + #region Protected Members + + protected override void Dispose(bool manual) + { + if (display != IntPtr.Zero) + { + Egl.Terminate(display); + display = IntPtr.Zero; + } + if (gbm_device != IntPtr.Zero) + { + Gbm.DestroyDevice(gbm_device); + gbm_device = IntPtr.Zero; + } + if (fd >= 0) + { + Libc.close(fd); + } + + base.Dispose(manual); + } + + #endregion + #region IPlatformFactory Members public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice display_device) @@ -144,7 +167,7 @@ namespace OpenTK.Platform.Linux public override IDisplayDeviceDriver CreateDisplayDeviceDriver() { - return DisplayDriver; + return new LinuxDisplayDriver(fd); } public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) diff --git a/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs index f42d1708..eaa6be42 100644 --- a/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs +++ b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs @@ -295,3 +295,5 @@ namespace OpenTK.Platform.Linux } } + +