mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Core - Split up RGBService into RenderService and DeviceService
Core - Removed rendering from CoreService Core - Store and restore original device LEDs when applying/clearing layouts
This commit is contained in:
1 parent
919199a8d0
commit
2a0a0f3cfd
43 files changed
+1363
-1189
No files matched your search
@@ -0,0 +1,262 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Providers;
|
||||
using Artemis.Core.Services.Core;
|
||||
using Artemis.Core.SkiaSharp;
|
||||
using DryIoc;
|
||||
using RGB.NET.Core;
|
||||
using Serilog;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
internal class RenderService : IRenderService, IRenderer, IDisposable
|
||||
{
|
||||
private readonly Stopwatch _frameStopWatch;
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly LazyEnumerable<IGraphicsContextProvider> _graphicsContextProviders;
|
||||
private readonly PluginSetting<int> _targetFrameRateSetting;
|
||||
private readonly PluginSetting<double> _renderScaleSetting;
|
||||
private readonly PluginSetting<string> _preferredGraphicsContext;
|
||||
|
||||
private SurfaceManager _surfaceManager;
|
||||
private int _frames;
|
||||
private DateTime _lastExceptionLog;
|
||||
private DateTime _lastFrameRateSample;
|
||||
private bool _initialized;
|
||||
|
||||
public RenderService(ILogger logger, ISettingsService settingsService, IDeviceService deviceService, LazyEnumerable<IGraphicsContextProvider> graphicsContextProviders)
|
||||
{
|
||||
_frameStopWatch = new Stopwatch();
|
||||
_logger = logger;
|
||||
_deviceService = deviceService;
|
||||
_graphicsContextProviders = graphicsContextProviders;
|
||||
|
||||
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.25);
|
||||
_preferredGraphicsContext = settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
||||
_targetFrameRateSetting.SettingChanged += OnRenderSettingsChanged;
|
||||
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;
|
||||
_preferredGraphicsContext.SettingChanged += PreferredGraphicsContextOnSettingChanged;
|
||||
|
||||
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||
_surfaceManager = new SurfaceManager(this, GraphicsContext, _targetFrameRateSetting.Value, (float) _renderScaleSetting.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IManagedGraphicsContext? GraphicsContext { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public RGBSurface Surface => _surfaceManager.Surface;
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<IRenderer> Renderers { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPaused
|
||||
{
|
||||
get => _surfaceManager.IsPaused;
|
||||
set => _surfaceManager.SetPaused(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int FrameRate { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeSpan FrameTime { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool FlushLeds { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Render(SKCanvas canvas, double delta)
|
||||
{
|
||||
_frameStopWatch.Restart();
|
||||
|
||||
if (FlushLeds)
|
||||
{
|
||||
FlushLeds = false;
|
||||
Surface.Update(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, delta, _surfaceManager.Surface));
|
||||
foreach (IRenderer renderer in Renderers)
|
||||
renderer.Render(canvas, delta);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_updateExceptions.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PostRender(SKTexture texture)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (IRenderer renderer in Renderers)
|
||||
renderer.PostRender(texture);
|
||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _surfaceManager.Surface));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_updateExceptions.Add(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_frameStopWatch.Stop();
|
||||
_frames++;
|
||||
|
||||
if ((DateTime.Now - _lastFrameRateSample).TotalSeconds >= 1)
|
||||
{
|
||||
FrameRate = _frames;
|
||||
_frames = 0;
|
||||
_lastFrameRateSample = DateTime.Now;
|
||||
}
|
||||
|
||||
FrameTime = _frameStopWatch.Elapsed;
|
||||
|
||||
LogUpdateExceptions();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetGraphicsContext()
|
||||
{
|
||||
if (Constants.StartupArguments.Contains("--force-software-render"))
|
||||
{
|
||||
_logger.Warning("Startup argument '--force-software-render' is applied, forcing software rendering");
|
||||
GraphicsContext = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_preferredGraphicsContext.Value == "Software")
|
||||
{
|
||||
GraphicsContext = null;
|
||||
return;
|
||||
}
|
||||
|
||||
List<IGraphicsContextProvider> providers = _graphicsContextProviders.ToList();
|
||||
if (!providers.Any())
|
||||
{
|
||||
_logger.Warning("No graphics context provider found, defaulting to software rendering");
|
||||
GraphicsContext = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
IManagedGraphicsContext? context = providers.FirstOrDefault(p => p.GraphicsContextName == _preferredGraphicsContext.Value)?.GetGraphicsContext();
|
||||
if (context == null)
|
||||
_logger.Warning("No graphics context named '{Context}' found, defaulting to software rendering", _preferredGraphicsContext.Value);
|
||||
|
||||
GraphicsContext = context;
|
||||
}
|
||||
}
|
||||
|
||||
private void LogUpdateExceptions()
|
||||
{
|
||||
// Only log update exceptions every 10 seconds to avoid spamming the logs
|
||||
if (DateTime.Now - _lastExceptionLog < TimeSpan.FromSeconds(10))
|
||||
return;
|
||||
_lastExceptionLog = DateTime.Now;
|
||||
|
||||
if (!_updateExceptions.Any())
|
||||
return;
|
||||
|
||||
// Group by stack trace, that should gather up duplicate exceptions
|
||||
foreach (IGrouping<string?, Exception> exceptions in _updateExceptions.GroupBy(e => e.StackTrace))
|
||||
_logger.Warning(exceptions.First(), "Exception was thrown {count} times during update in the last 10 seconds", exceptions.Count());
|
||||
|
||||
// When logging is finished start with a fresh slate
|
||||
_updateExceptions.Clear();
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDeviceProviderAdded(object? sender, DeviceProviderEventArgs e)
|
||||
{
|
||||
_surfaceManager.AddDevices(e.Devices.Where(d => d.IsEnabled));
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDeviceProviderRemoved(object? sender, DeviceProviderEventArgs e)
|
||||
{
|
||||
_surfaceManager.RemoveDevices(e.Devices);
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDeviceEnabled(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
_surfaceManager.AddDevices(new List<ArtemisDevice> {e.Device});
|
||||
}
|
||||
|
||||
private void DeviceServiceOnDeviceDisabled(object? sender, DeviceEventArgs e)
|
||||
{
|
||||
_surfaceManager.RemoveDevices(new List<ArtemisDevice> {e.Device});
|
||||
}
|
||||
|
||||
private void OnRenderSettingsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
_surfaceManager.UpdateTargetFrameRate(_targetFrameRateSetting.Value);
|
||||
}
|
||||
|
||||
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
RenderScale.SetRenderScaleMultiplier((int) (1 / _renderScaleSetting.Value));
|
||||
_surfaceManager.UpdateRenderScale((float) _renderScaleSetting.Value);
|
||||
}
|
||||
|
||||
private void PreferredGraphicsContextOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
SetGraphicsContext();
|
||||
_surfaceManager.UpdateGraphicsContext(GraphicsContext);
|
||||
}
|
||||
|
||||
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||
{
|
||||
IsPaused = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
IsPaused = true;
|
||||
_surfaceManager.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<FrameRenderingEventArgs>? FrameRendering;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<FrameRenderedEventArgs>? FrameRendered;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
if (_initialized)
|
||||
return;
|
||||
|
||||
SetGraphicsContext();
|
||||
_surfaceManager.AddDevices(_deviceService.EnabledDevices);
|
||||
|
||||
_deviceService.DeviceProviderAdded += DeviceServiceOnDeviceProviderAdded;
|
||||
_deviceService.DeviceProviderRemoved += DeviceServiceOnDeviceProviderRemoved;
|
||||
_deviceService.DeviceEnabled += DeviceServiceOnDeviceEnabled;
|
||||
_deviceService.DeviceDisabled += DeviceServiceOnDeviceDisabled;
|
||||
|
||||
IsPaused = false;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
protected virtual void OnFrameRendering(FrameRenderingEventArgs e)
|
||||
{
|
||||
FrameRendering?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnFrameRendered(FrameRenderedEventArgs e)
|
||||
{
|
||||
FrameRendered?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user