mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Device visualizer - Never return NaN measurements
Device logical layout picker - Fixed NRE Surface manager - Improve thread safety
This commit is contained in:
parent
7e72e22295
commit
38eb0ff460
@ -22,8 +22,8 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
|
||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
||||
{
|
||||
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||
Rectangle ledRectangle = new(rgbDevice.Select(x => x.Boundary));
|
||||
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||
_originalSize = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
||||
|
||||
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||
@ -54,10 +54,10 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
|
||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
||||
{
|
||||
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||
Rectangle ledRectangle = new(rgbDevice.Select(x => x.Boundary));
|
||||
_originalLeds = new List<OriginalLed>(rgbDevice.Select(l => new OriginalLed(l)));
|
||||
_originalSize = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
||||
|
||||
|
||||
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||
DeviceEntity = deviceEntity;
|
||||
RgbDevice = rgbDevice;
|
||||
|
||||
@ -16,7 +16,6 @@ internal sealed class SurfaceManager : IDisposable
|
||||
private readonly TimerUpdateTrigger _updateTrigger;
|
||||
private readonly List<ArtemisDevice> _devices = new();
|
||||
private readonly SKTextureBrush _textureBrush = new(null) {CalculationMode = RenderMode.Absolute};
|
||||
private readonly object _renderLock = new();
|
||||
|
||||
private ListLedGroup? _surfaceLedGroup;
|
||||
private SKTexture? _texture;
|
||||
@ -24,7 +23,7 @@ internal sealed class SurfaceManager : IDisposable
|
||||
public SurfaceManager(IRenderer renderer, IManagedGraphicsContext? graphicsContext, int targetFrameRate, float renderScale)
|
||||
{
|
||||
_renderer = renderer;
|
||||
_updateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / targetFrameRate};
|
||||
_updateTrigger = new TimerUpdateTrigger(false) {UpdateFrequency = 1.0 / targetFrameRate};
|
||||
|
||||
GraphicsContext = graphicsContext;
|
||||
TargetFrameRate = targetFrameRate;
|
||||
@ -45,34 +44,46 @@ internal sealed class SurfaceManager : IDisposable
|
||||
|
||||
public void AddDevices(IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
lock (_renderLock)
|
||||
List<IRGBDevice> newDevices = new();
|
||||
lock (_devices)
|
||||
{
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
{
|
||||
if (_devices.Contains(artemisDevice))
|
||||
continue;
|
||||
_devices.Add(artemisDevice);
|
||||
Surface.Attach(artemisDevice.RgbDevice);
|
||||
newDevices.Add(artemisDevice.RgbDevice);
|
||||
artemisDevice.DeviceUpdated += ArtemisDeviceOnDeviceUpdated;
|
||||
}
|
||||
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
if (!newDevices.Any())
|
||||
return;
|
||||
|
||||
Surface.Attach(newDevices);
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
public void RemoveDevices(IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
lock (_renderLock)
|
||||
List<IRGBDevice> removedDevices = new();
|
||||
lock (_devices)
|
||||
{
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
{
|
||||
if (!_devices.Remove(artemisDevice))
|
||||
continue;
|
||||
artemisDevice.DeviceUpdated -= ArtemisDeviceOnDeviceUpdated;
|
||||
Surface.Detach(artemisDevice.RgbDevice);
|
||||
removedDevices.Add(artemisDevice.RgbDevice);
|
||||
_devices.Remove(artemisDevice);
|
||||
}
|
||||
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
if (!removedDevices.Any())
|
||||
return;
|
||||
|
||||
Surface.Detach(removedDevices);
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
public bool SetPaused(bool paused)
|
||||
@ -97,20 +108,14 @@ internal sealed class SurfaceManager : IDisposable
|
||||
|
||||
public void UpdateRenderScale(float renderScale)
|
||||
{
|
||||
lock (_renderLock)
|
||||
{
|
||||
RenderScale = renderScale;
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
RenderScale = renderScale;
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
public void UpdateGraphicsContext(IManagedGraphicsContext? graphicsContext)
|
||||
{
|
||||
lock (_renderLock)
|
||||
{
|
||||
GraphicsContext = graphicsContext;
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
GraphicsContext = graphicsContext;
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -118,29 +123,10 @@ internal sealed class SurfaceManager : IDisposable
|
||||
{
|
||||
SetPaused(true);
|
||||
Surface.UnregisterUpdateTrigger(_updateTrigger);
|
||||
lock (_renderLock)
|
||||
{
|
||||
_updateTrigger.Dispose();
|
||||
_texture?.Dispose();
|
||||
Surface.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLedGroup()
|
||||
{
|
||||
List<Led> leds = _devices.SelectMany(d => d.Leds).Select(l => l.RgbLed).ToList();
|
||||
|
||||
if (_surfaceLedGroup == null)
|
||||
{
|
||||
_surfaceLedGroup = new ListLedGroup(Surface, leds) {Brush = _textureBrush};
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up the old background
|
||||
_surfaceLedGroup.Detach();
|
||||
|
||||
// Apply the application wide brush and decorator
|
||||
_surfaceLedGroup = new ListLedGroup(Surface, leds) {Brush = _textureBrush};
|
||||
_updateTrigger.Dispose();
|
||||
_texture?.Dispose();
|
||||
Surface.Dispose();
|
||||
}
|
||||
|
||||
private SKTexture CreateTexture()
|
||||
@ -155,53 +141,54 @@ internal sealed class SurfaceManager : IDisposable
|
||||
int width = Math.Max(1, MathF.Min(evenWidth * RenderScale, 4096).RoundToInt());
|
||||
int height = Math.Max(1, MathF.Min(evenHeight * RenderScale, 4096).RoundToInt());
|
||||
|
||||
_texture?.Dispose();
|
||||
_texture = new SKTexture(GraphicsContext, width, height, RenderScale, _devices);
|
||||
_textureBrush.Texture = _texture;
|
||||
lock (_devices)
|
||||
{
|
||||
_texture?.Dispose();
|
||||
_texture = new SKTexture(GraphicsContext, width, height, RenderScale, _devices);
|
||||
_textureBrush.Texture = _texture;
|
||||
|
||||
_surfaceLedGroup?.Detach();
|
||||
_surfaceLedGroup = new ListLedGroup(Surface, _devices.SelectMany(d => d.Leds).Select(l => l.RgbLed)) {Brush = _textureBrush};
|
||||
}
|
||||
|
||||
UpdateLedGroup();
|
||||
|
||||
return _texture;
|
||||
}
|
||||
|
||||
private void SurfaceOnUpdating(UpdatingEventArgs args)
|
||||
{
|
||||
lock (_renderLock)
|
||||
SKTexture? texture = _texture;
|
||||
if (texture == null || texture.IsInvalid)
|
||||
texture = CreateTexture();
|
||||
|
||||
// Prepare a canvas
|
||||
SKCanvas canvas = texture.Surface.Canvas;
|
||||
canvas.Save();
|
||||
|
||||
// Apply scaling if necessary
|
||||
if (Math.Abs(texture.RenderScale - 1) > 0.001)
|
||||
canvas.Scale(texture.RenderScale);
|
||||
|
||||
// Fresh start!
|
||||
canvas.Clear(new SKColor(0, 0, 0));
|
||||
|
||||
try
|
||||
{
|
||||
SKTexture? texture = _texture;
|
||||
if (texture == null || texture.IsInvalid)
|
||||
texture = CreateTexture();
|
||||
_renderer.Render(canvas, args.DeltaTime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
canvas.RestoreToCount(-1);
|
||||
canvas.Flush();
|
||||
texture.CopyPixelData();
|
||||
}
|
||||
|
||||
// Prepare a canvas
|
||||
SKCanvas canvas = texture.Surface.Canvas;
|
||||
canvas.Save();
|
||||
|
||||
// Apply scaling if necessary
|
||||
if (Math.Abs(texture.RenderScale - 1) > 0.001)
|
||||
canvas.Scale(texture.RenderScale);
|
||||
|
||||
// Fresh start!
|
||||
canvas.Clear(new SKColor(0, 0, 0));
|
||||
|
||||
try
|
||||
{
|
||||
_renderer.Render(canvas, args.DeltaTime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
canvas.RestoreToCount(-1);
|
||||
canvas.Flush();
|
||||
texture.CopyPixelData();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_renderer.PostRender(texture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
try
|
||||
{
|
||||
_renderer.PostRender(texture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -153,9 +153,9 @@ public class DeviceVisualizer : Control
|
||||
|
||||
private Rect MeasureDevice()
|
||||
{
|
||||
if (Device == null)
|
||||
if (Device == null || float.IsNaN(Device.RgbDevice.ActualSize.Width) || float.IsNaN(Device.RgbDevice.ActualSize.Height))
|
||||
return new Rect();
|
||||
|
||||
|
||||
Rect deviceRect = new(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
|
||||
Geometry geometry = new RectangleGeometry(deviceRect);
|
||||
geometry.Transform = new RotateTransform(Device.Rotation);
|
||||
|
||||
@ -10,8 +10,6 @@ namespace Artemis.UI.Screens.Device;
|
||||
|
||||
public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl<DeviceLogicalLayoutDialogViewModel>
|
||||
{
|
||||
private readonly AutoCompleteBox _autoCompleteBox;
|
||||
|
||||
public DeviceLogicalLayoutDialogView()
|
||||
{
|
||||
InitializeComponent();
|
||||
@ -23,8 +21,8 @@ public partial class DeviceLogicalLayoutDialogView : ReactiveUserControl<DeviceL
|
||||
private async Task DelayedAutoFocus()
|
||||
{
|
||||
await Task.Delay(200);
|
||||
_autoCompleteBox.Focus();
|
||||
_autoCompleteBox.PopulateComplete();
|
||||
RegionsAutoCompleteBox.Focus();
|
||||
RegionsAutoCompleteBox.PopulateComplete();
|
||||
}
|
||||
|
||||
private bool SearchRegions(string search, object item)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user