[KMS] Added DRM/GBM framebuffer implementation
This commit is contained in:
parent
19b34446bb
commit
753032b844
12 changed files with 651 additions and 115 deletions
|
@ -813,6 +813,8 @@
|
|||
</Compile>
|
||||
<Compile Include="Platform\Linux\Bindings\Libc.cs" />
|
||||
<Compile Include="Platform\Linux\LinuxWindowInfo.cs" />
|
||||
<Compile Include="Platform\Linux\LinuxGraphicsContext.cs" />
|
||||
<Compile Include="Platform\Linux\Bindings\Poll.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -33,7 +33,7 @@ using System.Text;
|
|||
|
||||
namespace OpenTK.Platform.Linux
|
||||
{
|
||||
class Libc
|
||||
partial class Libc
|
||||
{
|
||||
const string lib = "libc";
|
||||
|
||||
|
|
65
Source/OpenTK/Platform/Linux/Bindings/Poll.cs
Normal file
65
Source/OpenTK/Platform/Linux/Bindings/Poll.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
#region License
|
||||
//
|
||||
// Poll.cs
|
||||
//
|
||||
// Author:
|
||||
// Stefanos A. <stapostol@gmail.com>
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -35,54 +35,80 @@ using OpenTK.Graphics;
|
|||
|
||||
namespace OpenTK.Platform.Linux
|
||||
{
|
||||
class LinuxDisplayDriver : DisplayDeviceBase
|
||||
// Stores platform-specific information about a display
|
||||
class LinuxDisplay
|
||||
{
|
||||
unsafe class LinuxDisplay
|
||||
{
|
||||
public ModeConnector* Connector;
|
||||
public ModeEncoder* Encoder;
|
||||
public ModeCrtc* Crtc;
|
||||
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
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Crtc == null)
|
||||
if (Crtc == IntPtr.Zero)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
return Crtc->mode;
|
||||
unsafe
|
||||
{
|
||||
return pCrtc->mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public ModeInfo OriginalMode;
|
||||
|
||||
public int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Crtc == null)
|
||||
if (Crtc == IntPtr.Zero)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
return (int)Crtc->crtc_id;
|
||||
unsafe
|
||||
{
|
||||
return (int)pCrtc->crtc_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxDisplay(ModeConnector* c, ModeEncoder* e, ModeCrtc* r)
|
||||
public LinuxDisplay(IntPtr c, IntPtr e, IntPtr r)
|
||||
{
|
||||
Connector = c;
|
||||
Encoder = e;
|
||||
Crtc = r;
|
||||
OriginalMode = Crtc->mode; // in case we change resolution later on
|
||||
unsafe
|
||||
{
|
||||
OriginalMode = pCrtc->mode; // in case we change resolution later on
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LinuxDisplayDriver : DisplayDeviceBase
|
||||
{
|
||||
readonly int FD;
|
||||
readonly Dictionary<int, int> DisplayIds =
|
||||
new Dictionary<int, int>();
|
||||
|
||||
public LinuxDisplayDriver(int fd)
|
||||
{
|
||||
Debug.Print("[KMS] Creating LinuxDisplayDriver for fd:{0}", fd);
|
||||
Debug.Indent();
|
||||
try
|
||||
{
|
||||
FD = fd;
|
||||
QueryDisplays();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Debug.Unindent();
|
||||
}
|
||||
}
|
||||
|
||||
void QueryDisplays()
|
||||
{
|
||||
|
@ -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<DisplayResolution> modes = new List<DisplayResolution>();
|
||||
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<DisplayResolution> modes = new List<DisplayResolution>(mode_count);
|
||||
for (int i = 0; i < mode_count; i++)
|
||||
{
|
||||
ModeInfo* mode = display.Connector->modes + i;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
297
Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
Normal file
297
Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
Normal file
|
@ -0,0 +1,297 @@
|
|||
#region License
|
||||
//
|
||||
// LinuxGraphicsContext.cs
|
||||
//
|
||||
// Author:
|
||||
// thefiddler <stapostol@gmail.com>
|
||||
//
|
||||
// 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
|
||||
/// <summary>
|
||||
/// Defines an IGraphicsContext implementation for the Linux KMS framebuffer.
|
||||
/// For Linux/X11 and other Unix operating systems, use the more generic
|
||||
/// <see cref="OpenTK.Platform.Egl.EglUnixContext"/> instead.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: to display our results, we need to allocate a GBM framebuffer
|
||||
/// and point the scanout address to that via Drm.ModeSetCrtc.
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}]",
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue