mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-02-24 19:17:01 +00:00
[KMS] Fixed NRE in Toolkit.Init(); improved resource cleanup
This commit is contained in:
parent
753032b844
commit
452bafdbb1
|
@ -61,6 +61,9 @@ namespace OpenTK.Platform.Linux
|
||||||
[DllImport(lib, EntryPoint = "drmModeRmFB", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport(lib, EntryPoint = "drmModeRmFB", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern int ModeRmFB(int fd, int bufferId);
|
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)]
|
[DllImport(lib, EntryPoint = "drmModeGetCrtc", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern IntPtr ModeGetCrtc(int fd, int crtcId);
|
public static extern IntPtr ModeGetCrtc(int fd, int crtcId);
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,9 @@ namespace OpenTK.Platform.Linux
|
||||||
[DllImport(lib, EntryPoint = "gbm_create_device", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport(lib, EntryPoint = "gbm_create_device", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern Device CreateDevice(int fd);
|
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)]
|
[DllImport(lib, EntryPoint = "gbm_device_get_fd", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern int DeviceGetFD(IntPtr gbm);
|
public static extern int DeviceGetFD(IntPtr gbm);
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace OpenTK.Platform.Linux
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FD = fd;
|
FD = fd;
|
||||||
QueryDisplays();
|
UpdateDisplays(fd);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -110,7 +110,76 @@ 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
|
||||||
|
{
|
||||||
|
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
|
unsafe
|
||||||
{
|
{
|
||||||
|
@ -119,33 +188,12 @@ namespace OpenTK.Platform.Linux
|
||||||
AvailableDevices.Clear();
|
AvailableDevices.Clear();
|
||||||
DisplayIds.Clear();
|
DisplayIds.Clear();
|
||||||
|
|
||||||
ModeRes* resources = (ModeRes*)Drm.ModeGetResources(FD);
|
List<LinuxDisplay> displays = new List<LinuxDisplay>();
|
||||||
if (resources == null)
|
if (QueryDisplays(fd, displays))
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("[KMS] DRM ModeGetResources failed");
|
foreach (LinuxDisplay display in displays)
|
||||||
}
|
|
||||||
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,
|
AddDisplay(display);
|
||||||
*(resources->connectors + i));
|
|
||||||
if (connector != null)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
ModeEncoder* encoder = null;
|
||||||
for (int i = 0; i < c->count_encoders && encoder == null; i++)
|
for (int i = 0; i < c->count_encoders && encoder == null; i++)
|
||||||
{
|
{
|
||||||
ModeEncoder* e = (ModeEncoder*)Drm.ModeGetEncoder(
|
ModeEncoder* e = (ModeEncoder*)Drm.ModeGetEncoder(
|
||||||
FD, *(c->encoders + i));
|
fd, *(c->encoders + i));
|
||||||
if (e != null)
|
if (e != null)
|
||||||
{
|
{
|
||||||
if (e->encoder_id == c->encoder_id)
|
if (e->encoder_id == c->encoder_id)
|
||||||
|
@ -186,10 +233,14 @@ namespace OpenTK.Platform.Linux
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Print("[KMS] Failed to find encoder for connector {0}", c->connector_id);
|
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)
|
if (crtc != null)
|
||||||
{
|
{
|
||||||
Debug.Print("[KMS] CRTC {0} found for encoder {1}",
|
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}",
|
Debug.Print("[KMS] Failed to find crtc {0} for encoder {1}",
|
||||||
encoder->crtc_id, encoder->encoder_id);
|
encoder->crtc_id, encoder->encoder_id);
|
||||||
return;
|
}
|
||||||
|
return crtc;
|
||||||
}
|
}
|
||||||
|
|
||||||
LinuxDisplay display = new LinuxDisplay((IntPtr)c, (IntPtr)encoder, (IntPtr)crtc);
|
unsafe static void GetModes(LinuxDisplay display, DisplayResolution[] modes, out DisplayResolution current)
|
||||||
if (!DisplayIds.ContainsKey(display.Id))
|
|
||||||
{
|
{
|
||||||
Debug.Print("[KMS] Adding display {0} as {1}", display.Id, AvailableDevices.Count);
|
|
||||||
DisplayIds.Add(display.Id, AvailableDevices.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mode_count = display.pConnector->count_modes;
|
int mode_count = display.pConnector->count_modes;
|
||||||
Debug.Print("[KMS] Display supports {0} modes", mode_count);
|
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++)
|
for (int i = 0; i < mode_count; i++)
|
||||||
{
|
{
|
||||||
ModeInfo* mode = display.pConnector->modes + i;
|
ModeInfo* mode = display.pConnector->modes + i;
|
||||||
|
@ -220,33 +266,89 @@ namespace OpenTK.Platform.Linux
|
||||||
Debug.Print("Mode {0}: {1}x{2} @{3}", i,
|
Debug.Print("Mode {0}: {1}x{2} @{3}", i,
|
||||||
mode->hdisplay, mode->vdisplay, mode->vrefresh);
|
mode->hdisplay, mode->vdisplay, mode->vrefresh);
|
||||||
DisplayResolution res = GetDisplayResolution(mode);
|
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
|
// Note: since we are not running a display manager, we are free
|
||||||
// to choose the display layout for multiple displays ourselves.
|
// to choose the display layout for multiple displays ourselves.
|
||||||
// We choose the simplest layout: displays are laid out side-by-side
|
// We choose the simplest layout: displays are laid out side-by-side
|
||||||
// from left to right. Primary display is the first display we encounter.
|
// from left to right. Primary display is the first display we encounter.
|
||||||
System.Drawing.Rectangle bounds =
|
int x = AvailableDevices.Count == 0 ?
|
||||||
new System.Drawing.Rectangle(
|
0 : AvailableDevices[AvailableDevices.Count - 1].Bounds.Right;
|
||||||
AvailableDevices.Count == 0 ? 0 : AvailableDevices[0].Bounds.Right,
|
int y = 0;
|
||||||
0,
|
|
||||||
current.Width,
|
return new System.Drawing.Rectangle(
|
||||||
current.Height);
|
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;
|
bool is_primary = AvailableDevices.Count == 0;
|
||||||
DisplayDevice device = new DisplayDevice(current, is_primary,
|
DisplayDevice device = new DisplayDevice(current, is_primary,
|
||||||
modes, bounds, display);
|
modes, GetBounds(current), display);
|
||||||
|
|
||||||
if (AvailableDevices.Count == 0)
|
if (is_primary)
|
||||||
{
|
{
|
||||||
Primary = device;
|
Primary = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateDisplayIndices(display, device);
|
||||||
|
|
||||||
Debug.Print("[KMS] Added DisplayDevice {0}", device);
|
Debug.Print("[KMS] Added DisplayDevice {0}", device);
|
||||||
AvailableDevices.Add(device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode)
|
unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode)
|
||||||
|
|
|
@ -69,8 +69,7 @@ namespace OpenTK.Platform.Linux
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DisplayDriver = new LinuxDisplayDriver(test_fd);
|
if (LinuxDisplayDriver.QueryDisplays(test_fd, null))
|
||||||
if (DisplayDriver.GetDisplay(DisplayIndex.Primary) != null)
|
|
||||||
{
|
{
|
||||||
fd = test_fd;
|
fd = test_fd;
|
||||||
break;
|
break;
|
||||||
|
@ -135,6 +134,30 @@ namespace OpenTK.Platform.Linux
|
||||||
|
|
||||||
#endregion
|
#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
|
#region IPlatformFactory Members
|
||||||
|
|
||||||
public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice display_device)
|
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()
|
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)
|
public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags)
|
||||||
|
|
|
@ -295,3 +295,5 @@ namespace OpenTK.Platform.Linux
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue