12 changed files with 828 additions and 67 deletions
@ -0,0 +1,292 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
|||
// ReSharper disable MemberCanBePrivate.Global
|
|||
// ReSharper disable FieldCanBeMadeReadOnly.Local
|
|||
|
|||
namespace Avalonia.LinuxFramebuffer.Output |
|||
{ |
|||
public enum DrmModeConnection |
|||
{ |
|||
DRM_MODE_CONNECTED = 1, |
|||
DRM_MODE_DISCONNECTED = 2, |
|||
DRM_MODE_UNKNOWNCONNECTION = 3 |
|||
} |
|||
|
|||
public enum DrmModeSubPixel{ |
|||
DRM_MODE_SUBPIXEL_UNKNOWN = 1, |
|||
DRM_MODE_SUBPIXEL_HORIZONTAL_RGB = 2, |
|||
DRM_MODE_SUBPIXEL_HORIZONTAL_BGR = 3, |
|||
DRM_MODE_SUBPIXEL_VERTICAL_RGB = 4, |
|||
DRM_MODE_SUBPIXEL_VERTICAL_BGR = 5, |
|||
DRM_MODE_SUBPIXEL_NONE = 6 |
|||
} |
|||
|
|||
static unsafe class LibDrm |
|||
{ |
|||
private const string libdrm = "libdrm.so.2"; |
|||
private const string libgbm = "libgbm.so.1"; |
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public unsafe delegate void DrmEventVBlankHandlerDelegate(int fd, |
|||
uint sequence, |
|||
uint tv_sec, |
|||
uint tv_usec, |
|||
void* user_data); |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public unsafe delegate void DrmEventPageFlipHandlerDelegate(int fd, |
|||
uint sequence, |
|||
uint tv_sec, |
|||
uint tv_usec, |
|||
void* user_data); |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public unsafe delegate IntPtr DrmEventPageFlipHandler2Delegate(int fd, |
|||
uint sequence, |
|||
uint tv_sec, |
|||
uint tv_usec, |
|||
uint crtc_id, |
|||
void* user_data); |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public unsafe delegate void DrmEventSequenceHandlerDelegate(int fd, |
|||
ulong sequence, |
|||
ulong ns, |
|||
ulong user_data); |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public struct DrmEventContext |
|||
{ |
|||
public int version; //4
|
|||
public IntPtr vblank_handler; |
|||
public IntPtr page_flip_handler; |
|||
public IntPtr page_flip_handler2; |
|||
public IntPtr sequence_handler; |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public struct drmModeRes { |
|||
|
|||
public int count_fbs; |
|||
public uint *fbs; |
|||
|
|||
public int count_crtcs; |
|||
public uint *crtcs; |
|||
|
|||
public int count_connectors; |
|||
public uint *connectors; |
|||
|
|||
public int count_encoders; |
|||
public uint *encoders; |
|||
|
|||
uint min_width, max_width; |
|||
uint min_height, max_height; |
|||
} |
|||
|
|||
[Flags] |
|||
public enum DrmModeType |
|||
{ |
|||
DRM_MODE_TYPE_BUILTIN = (1 << 0), |
|||
DRM_MODE_TYPE_CLOCK_C = ((1 << 1) | DRM_MODE_TYPE_BUILTIN), |
|||
DRM_MODE_TYPE_CRTC_C = ((1 << 2) | DRM_MODE_TYPE_BUILTIN), |
|||
DRM_MODE_TYPE_PREFERRED = (1 << 3), |
|||
DRM_MODE_TYPE_DEFAULT = (1 << 4), |
|||
DRM_MODE_TYPE_USERDEF = (1 << 5), |
|||
DRM_MODE_TYPE_DRIVER = (1 << 6) |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public struct drmModeModeInfo |
|||
{ |
|||
public uint clock; |
|||
public ushort hdisplay, hsync_start, hsync_end, htotal, hskew; |
|||
public ushort vdisplay, vsync_start, vsync_end, vtotal, vscan; |
|||
|
|||
public uint vrefresh; |
|||
|
|||
public uint flags; |
|||
public DrmModeType type; |
|||
public fixed byte name[32]; |
|||
public PixelSize Resolution => new PixelSize(hdisplay, vdisplay); |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public struct drmModeConnector { |
|||
public uint connector_id; |
|||
public uint encoder_id; /**< Encoder currently connected to */ |
|||
public uint connector_type; |
|||
public uint connector_type_id; |
|||
public DrmModeConnection connection; |
|||
public uint mmWidth, mmHeight; /**< HxW in millimeters */ |
|||
public DrmModeSubPixel subpixel; |
|||
|
|||
public int count_modes; |
|||
public drmModeModeInfo* modes; |
|||
|
|||
public int count_props; |
|||
public uint *props; /**< List of property ids */ |
|||
public ulong *prop_values; /**< List of property values */ |
|||
|
|||
public int count_encoders; |
|||
public uint *encoders; /**< List of encoder ids */ |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public struct drmModeEncoder { |
|||
public uint encoder_id; |
|||
public uint encoder_type; |
|||
public uint crtc_id; |
|||
public uint possible_crtcs; |
|||
public uint possible_clones; |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public struct drmModeCrtc { |
|||
public uint crtc_id; |
|||
public uint buffer_id; /**< FB id to connect to 0 = disconnect */ |
|||
|
|||
public uint x, y; /**< Position on the framebuffer */ |
|||
public uint width, height; |
|||
public int mode_valid; |
|||
public drmModeModeInfo mode; |
|||
|
|||
public int gamma_size; /**< Number of gamma stops */ |
|||
|
|||
} |
|||
|
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern drmModeRes* drmModeGetResources(int fd); |
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern void drmModeFreeResources(drmModeRes* res); |
|||
|
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern drmModeConnector* drmModeGetConnector(int fd, uint connector); |
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern void drmModeFreeConnector(drmModeConnector* res); |
|||
|
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern drmModeEncoder* drmModeGetEncoder(int fd, uint id); |
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern void drmModeFreeEncoder(drmModeEncoder* enc); |
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern drmModeCrtc* drmModeGetCrtc(int fd, uint id); |
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern void drmModeFreeCrtc(drmModeCrtc* enc); |
|||
|
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern int drmModeAddFB(int fd, uint width, uint height, byte depth, |
|||
byte bpp, uint pitch, uint bo_handle, |
|||
out uint buf_id); |
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern int drmModeSetCrtc(int fd, uint crtcId, uint bufferId, |
|||
uint x, uint y, uint *connectors, int count, |
|||
drmModeModeInfo* mode); |
|||
|
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern void drmModeRmFB(int fd, int id); |
|||
|
|||
[Flags] |
|||
public enum DrmModePageFlip |
|||
{ |
|||
Event = 1, |
|||
Async = 2, |
|||
Absolute = 4, |
|||
Relative = 8, |
|||
} |
|||
|
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern void drmModePageFlip(int fd, uint crtc_id, uint fb_id, |
|||
DrmModePageFlip flags, void *user_data); |
|||
|
|||
|
|||
[DllImport(libdrm, SetLastError = true)] |
|||
public static extern void drmHandleEvent(int fd, DrmEventContext* context); |
|||
|
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern IntPtr gbm_create_device(int fd); |
|||
|
|||
|
|||
[Flags] |
|||
public enum GbmBoFlags { |
|||
/** |
|||
* Buffer is going to be presented to the screen using an API such as KMS |
|||
*/ |
|||
GBM_BO_USE_SCANOUT = (1 << 0), |
|||
/** |
|||
* Buffer is going to be used as cursor |
|||
*/ |
|||
GBM_BO_USE_CURSOR = (1 << 1), |
|||
/** |
|||
* Deprecated |
|||
*/ |
|||
GBM_BO_USE_CURSOR_64X64 = GBM_BO_USE_CURSOR, |
|||
/** |
|||
* Buffer is to be used for rendering - for example it is going to be used |
|||
* as the storage for a color buffer |
|||
*/ |
|||
GBM_BO_USE_RENDERING = (1 << 2), |
|||
/** |
|||
* Buffer can be used for gbm_bo_write. This is guaranteed to work |
|||
* with GBM_BO_USE_CURSOR, but may not work for other combinations. |
|||
*/ |
|||
GBM_BO_USE_WRITE = (1 << 3), |
|||
/** |
|||
* Buffer is linear, i.e. not tiled. |
|||
*/ |
|||
GBM_BO_USE_LINEAR = (1 << 4), |
|||
}; |
|||
|
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern IntPtr gbm_surface_create(IntPtr device, int width, int height, uint format, GbmBoFlags flags); |
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern IntPtr gbm_surface_lock_front_buffer(IntPtr surface); |
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern int gbm_surface_release_buffer(IntPtr surface, IntPtr bo); |
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern IntPtr gbm_bo_get_user_data(IntPtr surface); |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void GbmBoUserDataDestroyCallbackDelegate(IntPtr bo, IntPtr data); |
|||
|
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern IntPtr gbm_bo_set_user_data(IntPtr bo, IntPtr userData, |
|||
GbmBoUserDataDestroyCallbackDelegate onFree); |
|||
|
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern uint gbm_bo_get_width(IntPtr bo); |
|||
|
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern uint gbm_bo_get_height(IntPtr bo); |
|||
|
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern uint gbm_bo_get_stride(IntPtr bo); |
|||
|
|||
|
|||
[StructLayout(LayoutKind.Explicit)] |
|||
public struct GbmBoHandle |
|||
{ |
|||
[FieldOffset(0)] |
|||
public void *ptr; |
|||
[FieldOffset(0)] |
|||
public int s32; |
|||
[FieldOffset(0)] |
|||
public uint u32; |
|||
[FieldOffset(0)] |
|||
public long s64; |
|||
[FieldOffset(0)] |
|||
public ulong u64; |
|||
} |
|||
|
|||
[DllImport(libgbm, SetLastError = true)] |
|||
public static extern ulong gbm_bo_get_handle(IntPtr bo); |
|||
|
|||
public static class GbmColorFormats |
|||
{ |
|||
public static uint FourCC(char a, char b, char c, char d) => |
|||
(uint)a | ((uint)b) << 8 | ((uint)c) << 16 | ((uint)d) << 24; |
|||
|
|||
public static uint GBM_FORMAT_XRGB8888 { get; } = FourCC('X', 'R', '2', '4'); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Runtime.InteropServices; |
|||
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods; |
|||
using static Avalonia.LinuxFramebuffer.Output.LibDrm; |
|||
|
|||
namespace Avalonia.LinuxFramebuffer.Output |
|||
{ |
|||
public unsafe class DrmConnector |
|||
{ |
|||
private static string[] KnownConnectorTypes = |
|||
{ |
|||
"None", "VGA", "DVI-I", "DVI-D", "DVI-A", "Composite", "S-Video", "LVDS", "Component", "DIN", |
|||
"DisplayPort", "HDMI-A", "HDMI-B", "TV", "eDP", "Virtual", "DSI" |
|||
}; |
|||
|
|||
public DrmModeConnection Connection { get; } |
|||
public uint Id { get; } |
|||
public string Name { get; } |
|||
public Size SizeMm { get; } |
|||
public DrmModeSubPixel SubPixel { get; } |
|||
internal uint EncoderId { get; } |
|||
internal List<uint> EncoderIds { get; } = new List<uint>(); |
|||
public List<DrmModeInfo> Modes { get; } = new List<DrmModeInfo>(); |
|||
internal DrmConnector(drmModeConnector* conn) |
|||
{ |
|||
Connection = conn->connection; |
|||
Id = conn->connector_id; |
|||
SizeMm = new Size(conn->mmWidth, conn->mmHeight); |
|||
SubPixel = conn->subpixel; |
|||
for (var c = 0; c < conn->count_encoders;c++) |
|||
EncoderIds.Add(conn->encoders[c]); |
|||
EncoderId = conn->encoder_id; |
|||
for(var c=0; c<conn->count_modes; c++) |
|||
Modes.Add(new DrmModeInfo(ref conn->modes[c])); |
|||
|
|||
if (conn->connector_type > KnownConnectorTypes.Length - 1) |
|||
Name = $"Unknown({conn->connector_type})-{conn->connector_type_id}"; |
|||
else |
|||
Name = KnownConnectorTypes[conn->connector_type] + "-" + conn->connector_type_id; |
|||
} |
|||
} |
|||
|
|||
public unsafe class DrmModeInfo |
|||
{ |
|||
internal drmModeModeInfo Mode; |
|||
|
|||
internal DrmModeInfo(ref drmModeModeInfo info) |
|||
{ |
|||
Mode = info; |
|||
fixed (void* pName = info.name) |
|||
Name = Marshal.PtrToStringAnsi(new IntPtr(pName)); |
|||
} |
|||
|
|||
public PixelSize Resolution => new PixelSize(Mode.hdisplay, Mode.vdisplay); |
|||
public bool IsPreferred => Mode.type.HasFlag(DrmModeType.DRM_MODE_TYPE_PREFERRED); |
|||
|
|||
public string Name { get; } |
|||
} |
|||
|
|||
unsafe class DrmEncoder |
|||
{ |
|||
public drmModeEncoder Encoder { get; } |
|||
public List<drmModeCrtc> PossibleCrtcs { get; } = new List<drmModeCrtc>(); |
|||
|
|||
public DrmEncoder(drmModeEncoder encoder, drmModeCrtc[] crtcs) |
|||
{ |
|||
Encoder = encoder; |
|||
for (var c = 0; c < crtcs.Length; c++) |
|||
{ |
|||
var bit = 1 << c; |
|||
if ((encoder.possible_crtcs & bit) != 0) |
|||
PossibleCrtcs.Add(crtcs[c]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
public unsafe class DrmResources |
|||
{ |
|||
public List<DrmConnector> Connectors { get; }= new List<DrmConnector>(); |
|||
internal Dictionary<uint, DrmEncoder> Encoders { get; } = new Dictionary<uint, DrmEncoder>(); |
|||
public DrmResources(int fd) |
|||
{ |
|||
var res = drmModeGetResources(fd); |
|||
if (res == null) |
|||
throw new Win32Exception("drmModeGetResources failed"); |
|||
|
|||
var crtcs = new drmModeCrtc[res->count_crtcs]; |
|||
for (var c = 0; c < res->count_crtcs; c++) |
|||
{ |
|||
var crtc = drmModeGetCrtc(fd, res->crtcs[c]); |
|||
crtcs[c] = *crtc; |
|||
drmModeFreeCrtc(crtc); |
|||
} |
|||
|
|||
for (var c = 0; c < res->count_encoders; c++) |
|||
{ |
|||
var enc = drmModeGetEncoder(fd, res->encoders[c]); |
|||
Encoders[res->encoders[c]] = new DrmEncoder(*enc, crtcs); |
|||
drmModeFreeEncoder(enc); |
|||
} |
|||
|
|||
for (var c = 0; c < res->count_connectors; c++) |
|||
{ |
|||
var conn = drmModeGetConnector(fd, res->connectors[c]); |
|||
Connectors.Add(new DrmConnector(conn)); |
|||
drmModeFreeConnector(conn); |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
public void Dump() |
|||
{ |
|||
void Print(int off, string s) |
|||
{ |
|||
for (var c = 0; c < off; c++) |
|||
Console.Write(" "); |
|||
Console.WriteLine(s); |
|||
} |
|||
Print(0, "Connectors"); |
|||
foreach (var conn in Connectors) |
|||
{ |
|||
Print(1, $"{conn.Name}:"); |
|||
Print(2, $"Id: {conn.Id}"); |
|||
Print(2, $"Size: {conn.SizeMm} mm"); |
|||
Print(2, $"Encoder id: {conn.EncoderId}"); |
|||
Print(2, "Modes"); |
|||
foreach (var m in conn.Modes) |
|||
Print(3, $"{m.Name} {(m.IsPreferred ? "PREFERRED" : "")}"); |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
public unsafe class DrmCard : IDisposable |
|||
{ |
|||
public int Fd { get; private set; } |
|||
public DrmCard(string path = null) |
|||
{ |
|||
path = path ?? "/dev/dri/card0"; |
|||
Fd = open(path, 2, 0); |
|||
if (Fd == -1) |
|||
throw new Win32Exception("Couldn't open " + path); |
|||
} |
|||
|
|||
public DrmResources GetResources() => new DrmResources(Fd); |
|||
public void Dispose() |
|||
{ |
|||
close(Fd); |
|||
Fd = -1; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,250 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.Platform.Interop; |
|||
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods; |
|||
using static Avalonia.LinuxFramebuffer.Output.LibDrm; |
|||
namespace Avalonia.LinuxFramebuffer.Output |
|||
{ |
|||
public unsafe class DrmOutput : IOutputBackend, IGlPlatformSurface, IWindowingPlatformGlFeature |
|||
{ |
|||
private DrmCard _card; |
|||
private readonly EglGlPlatformSurface _eglPlatformSurface; |
|||
public PixelSize PixelSize => _mode.Resolution; |
|||
|
|||
public DrmOutput(string path = null) |
|||
{ |
|||
var card = new DrmCard(path); |
|||
|
|||
var resources = card.GetResources(); |
|||
|
|||
|
|||
var connector = |
|||
resources.Connectors.FirstOrDefault(x => x.Connection == DrmModeConnection.DRM_MODE_CONNECTED); |
|||
if(connector == null) |
|||
throw new InvalidOperationException("Unable to find connected DRM connector"); |
|||
|
|||
var mode = connector.Modes.OrderByDescending(x => x.IsPreferred) |
|||
.ThenByDescending(x => x.Resolution.Width * x.Resolution.Height) |
|||
//.OrderByDescending(x => x.Resolution.Width * x.Resolution.Height)
|
|||
.FirstOrDefault(); |
|||
if(mode == null) |
|||
throw new InvalidOperationException("Unable to find a usable DRM mode"); |
|||
Init(card, resources, connector, mode); |
|||
} |
|||
|
|||
public DrmOutput(DrmCard card, DrmResources resources, DrmConnector connector, DrmModeInfo modeInfo) |
|||
{ |
|||
Init(card, resources, connector, modeInfo); |
|||
} |
|||
|
|||
[DllImport("libEGL.so.1")] |
|||
static extern IntPtr eglGetProcAddress(Utf8Buffer proc); |
|||
|
|||
private GbmBoUserDataDestroyCallbackDelegate FbDestroyDelegate; |
|||
private drmModeModeInfo _mode; |
|||
private EglDisplay _eglDisplay; |
|||
private EglSurface _eglSurface; |
|||
private EglContext _immediateContext; |
|||
private EglContext _deferredContext; |
|||
private IntPtr _currentBo; |
|||
private IntPtr _gbmTargetSurface; |
|||
private uint _crtcId; |
|||
|
|||
void FbDestroyCallback(IntPtr bo, IntPtr userData) |
|||
{ |
|||
drmModeRmFB(_card.Fd, userData.ToInt32()); |
|||
} |
|||
|
|||
uint GetFbIdForBo(IntPtr bo) |
|||
{ |
|||
if (bo == IntPtr.Zero) |
|||
throw new ArgumentException("bo is 0"); |
|||
var data = gbm_bo_get_user_data(bo); |
|||
if (data != IntPtr.Zero) |
|||
return (uint)data.ToInt32(); |
|||
|
|||
var w = gbm_bo_get_width(bo); |
|||
var h = gbm_bo_get_height(bo); |
|||
var stride = gbm_bo_get_stride(bo); |
|||
var handle = gbm_bo_get_handle(bo); |
|||
|
|||
var ret = drmModeAddFB(_card.Fd, w, h, 24, 32, stride, (uint)handle, out var fbHandle); |
|||
if (ret != 0) |
|||
throw new Win32Exception(ret, "drmModeAddFb failed"); |
|||
|
|||
gbm_bo_set_user_data(bo, new IntPtr((int)fbHandle), FbDestroyDelegate); |
|||
|
|||
|
|||
return fbHandle; |
|||
} |
|||
|
|||
|
|||
void Init(DrmCard card, DrmResources resources, DrmConnector connector, DrmModeInfo modeInfo) |
|||
{ |
|||
FbDestroyDelegate = FbDestroyCallback; |
|||
_card = card; |
|||
uint GetCrtc() |
|||
{ |
|||
if (resources.Encoders.TryGetValue(connector.EncoderId, out var encoder)) |
|||
{ |
|||
// Not sure why that should work
|
|||
return encoder.Encoder.crtc_id; |
|||
} |
|||
else |
|||
{ |
|||
foreach (var encId in connector.EncoderIds) |
|||
{ |
|||
if (resources.Encoders.TryGetValue(encId, out encoder) |
|||
&& encoder.PossibleCrtcs.Count>0) |
|||
return encoder.PossibleCrtcs.First().crtc_id; |
|||
} |
|||
|
|||
throw new InvalidOperationException("Unable to find CRTC matching the desired mode"); |
|||
} |
|||
} |
|||
|
|||
_crtcId = GetCrtc(); |
|||
var device = gbm_create_device(card.Fd); |
|||
_gbmTargetSurface = gbm_surface_create(device, modeInfo.Resolution.Width, modeInfo.Resolution.Height, |
|||
GbmColorFormats.GBM_FORMAT_XRGB8888, GbmBoFlags.GBM_BO_USE_SCANOUT | GbmBoFlags.GBM_BO_USE_RENDERING); |
|||
if(_gbmTargetSurface == null) |
|||
throw new InvalidOperationException("Unable to create GBM surface"); |
|||
|
|||
|
|||
|
|||
_eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), 0x31D7, device, null); |
|||
_eglSurface = _eglDisplay.CreateWindowSurface(_gbmTargetSurface); |
|||
|
|||
|
|||
EglContext CreateContext(EglContext share) |
|||
{ |
|||
var offSurf = gbm_surface_create(device, 1, 1, GbmColorFormats.GBM_FORMAT_XRGB8888, |
|||
GbmBoFlags.GBM_BO_USE_RENDERING); |
|||
if (offSurf == null) |
|||
throw new InvalidOperationException("Unable to create 1x1 sized GBM surface"); |
|||
return _eglDisplay.CreateContext(share, _eglDisplay.CreateWindowSurface(offSurf)); |
|||
} |
|||
|
|||
_immediateContext = CreateContext(null); |
|||
_deferredContext = CreateContext(_immediateContext); |
|||
|
|||
_immediateContext.MakeCurrent(_eglSurface); |
|||
_eglDisplay.GlInterface.ClearColor(0, 0, 0, 0); |
|||
_eglDisplay.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); |
|||
_eglSurface.SwapBuffers(); |
|||
var bo = gbm_surface_lock_front_buffer(_gbmTargetSurface); |
|||
var fbId = GetFbIdForBo(bo); |
|||
var connectorId = connector.Id; |
|||
var mode = modeInfo.Mode; |
|||
|
|||
|
|||
var res = drmModeSetCrtc(_card.Fd, _crtcId, fbId, 0, 0, &connectorId, 1, &mode); |
|||
if (res != 0) |
|||
throw new Win32Exception(res, "drmModeSetCrtc failed"); |
|||
|
|||
_mode = mode; |
|||
_currentBo = bo; |
|||
|
|||
// Go trough two cycles of buffer swapping (there are render artifacts otherwise)
|
|||
for(var c=0;c<2;c++) |
|||
using (CreateGlRenderTarget().BeginDraw()) |
|||
{ |
|||
_eglDisplay.GlInterface.ClearColor(0, 0, 0, 0); |
|||
_eglDisplay.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); |
|||
} |
|||
} |
|||
|
|||
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() |
|||
{ |
|||
return new RenderTarget(this); |
|||
} |
|||
|
|||
class RenderTarget : IGlPlatformSurfaceRenderTarget |
|||
{ |
|||
private readonly DrmOutput _parent; |
|||
|
|||
public RenderTarget(DrmOutput parent) |
|||
{ |
|||
_parent = parent; |
|||
} |
|||
public void Dispose() |
|||
{ |
|||
// We are wrapping GBM buffer chain associated with CRTC, and don't free it on a whim
|
|||
} |
|||
|
|||
class RenderSession : IGlPlatformSurfaceRenderingSession |
|||
{ |
|||
private readonly DrmOutput _parent; |
|||
|
|||
public RenderSession(DrmOutput parent) |
|||
{ |
|||
_parent = parent; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_parent._eglDisplay.GlInterface.Flush(); |
|||
_parent._eglSurface.SwapBuffers(); |
|||
|
|||
var nextBo = gbm_surface_lock_front_buffer(_parent._gbmTargetSurface); |
|||
if (nextBo == IntPtr.Zero) |
|||
{ |
|||
// Not sure what else can be done
|
|||
Console.WriteLine("gbm_surface_lock_front_buffer failed"); |
|||
} |
|||
else |
|||
{ |
|||
|
|||
var fb = _parent.GetFbIdForBo(nextBo); |
|||
bool waitingForFlip = true; |
|||
|
|||
drmModePageFlip(_parent._card.Fd, _parent._crtcId, fb, DrmModePageFlip.Event, null); |
|||
|
|||
DrmEventPageFlipHandlerDelegate flipCb = |
|||
(int fd, uint sequence, uint tv_sec, uint tv_usec, void* user_data) => |
|||
{ |
|||
waitingForFlip = false; |
|||
}; |
|||
var cbHandle = GCHandle.Alloc(flipCb); |
|||
var ctx = new DrmEventContext |
|||
{ |
|||
version = 4, page_flip_handler = Marshal.GetFunctionPointerForDelegate(flipCb) |
|||
}; |
|||
while (waitingForFlip) |
|||
{ |
|||
var pfd = new pollfd {events = 1, fd = _parent._card.Fd}; |
|||
poll(&pfd, new IntPtr(1), -1); |
|||
drmHandleEvent(_parent._card.Fd, &ctx); |
|||
} |
|||
|
|||
cbHandle.Free(); |
|||
gbm_surface_release_buffer(_parent._gbmTargetSurface, _parent._currentBo); |
|||
_parent._currentBo = nextBo; |
|||
} |
|||
_parent._eglDisplay.ClearContext(); |
|||
} |
|||
|
|||
|
|||
public IGlDisplay Display => _parent._eglDisplay; |
|||
|
|||
public PixelSize Size => _parent._mode.Resolution; |
|||
|
|||
public double Scaling => 1; |
|||
} |
|||
|
|||
public IGlPlatformSurfaceRenderingSession BeginDraw() |
|||
{ |
|||
_parent._deferredContext.MakeCurrent(_parent._eglSurface); |
|||
return new RenderSession(_parent); |
|||
} |
|||
} |
|||
|
|||
IGlContext IWindowingPlatformGlFeature.ImmediateContext => _immediateContext; |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Avalonia.LinuxFramebuffer.Output |
|||
{ |
|||
public interface IOutputBackend |
|||
{ |
|||
PixelSize PixelSize { get; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue