[KMS] Fixed NRE in Toolkit.Init(); improved resource cleanup
authorthefiddler <stapostol@gmail.com>
Thu, 26 Jun 2014 19:46:04 +0000 (21:46 +0200)
committerthefiddler <stapostol@gmail.com>
Wed, 16 Jul 2014 12:28:27 +0000 (14:28 +0200)
Source/OpenTK/Platform/Linux/Bindings/Drm.cs
Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
Source/OpenTK/Platform/Linux/LinuxFactory.cs
Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs

index 06f43d6..c070483 100644 (file)
@@ -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);
 
index 4c11eda..05e1ffd 100644 (file)
@@ -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);
 
index 14f6fb8..ad38c52 100644 (file)
@@ -102,7 +102,7 @@ namespace OpenTK.Platform.Linux
             try
             {
                 FD = fd;
-                QueryDisplays();
+                UpdateDisplays(fd);
             }
             finally
             {
@@ -110,43 +110,91 @@ namespace OpenTK.Platform.Linux
             }
         }
 
-        void QueryDisplays()
+        /// \internal
+        /// <summary>
+        /// Queries the specified GPU for connected displays and, optionally,
+        /// returns the list of displays.
+        /// </summary>
+        /// <returns><c>true</c>, if at least one display is connected, <c>false</c> otherwise.</returns>
+        /// <param name="fd">The fd for the GPU to query, obtained through open("/dev/dri/card0").</param>
+        /// <param name="displays">
+        /// If not null, this will contain a list <see cref="LinuxDisplay"/> instances,
+        /// one for each connected display.
+        /// </param>
+        internal static bool QueryDisplays(int fd, List<LinuxDisplay> displays)
         {
             unsafe
             {
-                lock (this)
+                bool has_displays = false;
+                if (displays != null)
                 {
-                    AvailableDevices.Clear();
-                    DisplayIds.Clear();
+                    displays.Clear();
+                }
 
-                    ModeRes* resources = (ModeRes*)Drm.ModeGetResources(FD);
-                    if (resources == null)
-                    {
-                        throw new NotSupportedException("[KMS] DRM ModeGetResources failed");
-                    }
-                    Debug.Print("[KMS] DRM found {0} connectors", resources->count_connectors);
+                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++)
+                // 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)
                     {
-                        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)
-                            {
-                                // Connector found!
-                                AddDisplay(connector);
-                            }
-                            else
+                            connector->count_modes > 0)
                             {
-                                // This is not the display we are looking for
-                                Drm.ModeFreeConnector((IntPtr)connector);
-                                connector = null;
+                                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
+            {
+                lock (this)
+                {
+                    AvailableDevices.Clear();
+                    DisplayIds.Clear();
+
+                    List<LinuxDisplay> displays = new List<LinuxDisplay>();
+                    if (QueryDisplays(fd, displays))
+                    {
+                        foreach (LinuxDisplay display in displays)
+                        {
+                            AddDisplay(display);
+                        }
                     }
 
                     if (AvailableDevices.Count == 0)
@@ -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<DisplayResolution> modes = new List<DisplayResolution>(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(&current_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)
index 11e5e80..45315d8 100644 (file)
@@ -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)