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