diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxDrmOptions.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxDrmOptions.cs new file mode 100644 index 0000000000..a67d6f7d8b --- /dev/null +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxDrmOptions.cs @@ -0,0 +1,46 @@ +using Avalonia.LinuxFramebuffer.Output; +using Avalonia.Media; +using JetBrains.Annotations; + +namespace Avalonia.LinuxFramebuffer +{ + public class LinuxDrmOptions + { + /// + /// Path for DrmCard to use, if no is passed. + /// Default: null + /// + [CanBeNull] public string Card { get; set; } + + /// + /// True to call drmModeGetConnector for all available connectors, otherwise drmModeGetConnectorCurrent is called to get the kernel-cached connected connector. + /// Info: since some hardware might have incorrect connector information on startup for some reason, you may need to set this parameter to true. + /// Default: False + /// + public bool DrmConnectorsForceProbe { get; set; } = false; + + /// + /// Scaling factor. + /// Default: 1.0 + /// + public double Scaling { get; set; } = 1.0; + + /// + /// If true an two cycle buffer swapping is processed at init. + /// Default: True + /// + public bool EnableInitialBufferSwapping { get; set; } = true; + + /// + /// Color for + /// Default: R0 G0 B0 A0 + /// + public Color InitialBufferSwappingColor { get; set; } = new Color(0, 0, 0, 0); + + /// + /// IOutputBackend. If Null, a new DrmOutput for with will be created. + /// Default: null + /// + [CanBeNull] public IOutputBackend OutputBackend { get; set; } + } +} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs index 4add4c423b..bf29aed1a4 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs @@ -140,6 +140,14 @@ public static class LinuxFramebufferPlatformExtensions public static int StartLinuxDrm(this T builder, string[] args, string card = null, double scaling = 1) where T : AppBuilderBase, new() => StartLinuxDirect(builder, args, new DrmOutput(card) {Scaling = scaling}); + + public static int StartLinuxDrm(this T builder, string[] args, [CanBeNull] LinuxDrmOptions drmOptions) + where T : AppBuilderBase, new() + { + drmOptions ??= new LinuxDrmOptions(); + drmOptions.OutputBackend ??= new DrmOutput(drmOptions) {Scaling = drmOptions.Scaling}; + return StartLinuxDirect(builder, args, drmOptions.OutputBackend); + } public static int StartLinuxDirect(this T builder, string[] args, IOutputBackend backend) where T : AppBuilderBase, new() diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs index 9c476b1b63..04bc2c1f67 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs @@ -162,6 +162,8 @@ namespace Avalonia.LinuxFramebuffer.Output [DllImport(libdrm, SetLastError = true)] public static extern drmModeConnector* drmModeGetConnector(int fd, uint connector); [DllImport(libdrm, SetLastError = true)] + public static extern drmModeConnector* drmModeGetConnectorCurrent(int fd, uint connector); + [DllImport(libdrm, SetLastError = true)] public static extern void drmModeFreeConnector(drmModeConnector* res); [DllImport(libdrm, SetLastError = true)] diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs index de281245f1..070e1a95bc 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs @@ -84,7 +84,7 @@ namespace Avalonia.LinuxFramebuffer.Output { public List Connectors { get; }= new List(); internal Dictionary Encoders { get; } = new Dictionary(); - public DrmResources(int fd) + public DrmResources(int fd, bool connectorsForceProbe = false) { var res = drmModeGetResources(fd); if (res == null) @@ -107,7 +107,7 @@ namespace Avalonia.LinuxFramebuffer.Output for (var c = 0; c < res->count_connectors; c++) { - var conn = drmModeGetConnector(fd, res->connectors[c]); + var conn = connectorsForceProbe ? drmModeGetConnector(fd, res->connectors[c]) : drmModeGetConnectorCurrent(fd, res->connectors[c]); Connectors.Add(new DrmConnector(conn)); drmModeFreeConnector(conn); } @@ -168,7 +168,7 @@ namespace Avalonia.LinuxFramebuffer.Output } } - public DrmResources GetResources() => new DrmResources(Fd); + public DrmResources GetResources(bool connectorsForceProbe = false) => new DrmResources(Fd, connectorsForceProbe); public void Dispose() { close(Fd); diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs index 99c4b62716..85cf1fba76 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs @@ -15,20 +15,34 @@ namespace Avalonia.LinuxFramebuffer.Output { public unsafe class DrmOutput : IGlOutputBackend, IGlPlatformSurface { + private LinuxDrmOptions _options; private DrmCard _card; public PixelSize PixelSize => _mode.Resolution; - public double Scaling { get; set; } + + public double Scaling + { + get => _options.Scaling; + set => _options.Scaling = value; + } public IGlContext PrimaryContext => _deferredContext; private EglPlatformOpenGlInterface _platformGl; public IPlatformOpenGlInterface PlatformOpenGlInterface => _platformGl; - public DrmOutput(string path = null) + public DrmOutput(LinuxDrmOptions drmOptions) { - var card = new DrmCard(path); + _options = drmOptions; + CreateDrmOutput(_options.Card, _options.DrmConnectorsForceProbe); + } + public DrmOutput(string path = null) : this(new LinuxDrmOptions() { Card = path }) + { + } - var resources = card.GetResources(); + private void CreateDrmOutput(string path = null, bool connectorsForceProbe = false) + { + var card = new DrmCard(path); + var resources = card.GetResources(connectorsForceProbe); var connector = resources.Connectors.FirstOrDefault(x => x.Connection == DrmModeConnection.DRM_MODE_CONNECTED); @@ -144,7 +158,9 @@ namespace Avalonia.LinuxFramebuffer.Output using (_deferredContext.MakeCurrent(_eglSurface)) { - _deferredContext.GlInterface.ClearColor(0, 0, 0, 0); + _deferredContext.GlInterface.ClearColor(_options.InitialBufferSwappingColor.R, + _options.InitialBufferSwappingColor.G, _options.InitialBufferSwappingColor.B, + _options.InitialBufferSwappingColor.A); _deferredContext.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); _eglSurface.SwapBuffers(); } @@ -162,13 +178,18 @@ namespace Avalonia.LinuxFramebuffer.Output _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()) - { - _deferredContext.GlInterface.ClearColor(0, 0, 0, 0); - _deferredContext.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); - } + if (_options.EnableInitialBufferSwapping) + { + //Go trough two cycles of buffer swapping (there are render artifacts otherwise) + for(var c=0;c<2;c++) + using (CreateGlRenderTarget().BeginDraw()) + { + _deferredContext.GlInterface.ClearColor(_options.InitialBufferSwappingColor.R, + _options.InitialBufferSwappingColor.G, _options.InitialBufferSwappingColor.B, + _options.InitialBufferSwappingColor.A); + _deferredContext.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); + } + } }