mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-02 10:43:31 +00:00
Profiles - Tweaked render pipeline, improving render performance by ~40%
This commit is contained in:
parent
7735edea1e
commit
991c5fd955
@ -29,7 +29,6 @@ namespace Artemis.Core
|
|||||||
Profile = Parent.Profile;
|
Profile = Parent.Profile;
|
||||||
Name = name;
|
Name = name;
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
Renderer = new Renderer();
|
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_expandedPropertyGroups = new List<string>();
|
_expandedPropertyGroups = new List<string>();
|
||||||
@ -47,7 +46,6 @@ namespace Artemis.Core
|
|||||||
Name = folderEntity.Name;
|
Name = folderEntity.Name;
|
||||||
Enabled = folderEntity.Enabled;
|
Enabled = folderEntity.Enabled;
|
||||||
Order = folderEntity.Order;
|
Order = folderEntity.Order;
|
||||||
Renderer = new Renderer();
|
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_expandedPropertyGroups = new List<string>();
|
_expandedPropertyGroups = new List<string>();
|
||||||
@ -69,8 +67,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal override RenderElementEntity RenderElementEntity => FolderEntity;
|
internal override RenderElementEntity RenderElementEntity => FolderEntity;
|
||||||
|
|
||||||
internal Renderer Renderer { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override List<ILayerProperty> GetAllLayerProperties()
|
public override List<ILayerProperty> GetAllLayerProperties()
|
||||||
{
|
{
|
||||||
@ -133,7 +129,7 @@ namespace Artemis.Core
|
|||||||
/// <returns>The newly created copy</returns>
|
/// <returns>The newly created copy</returns>
|
||||||
public Folder CreateCopy()
|
public Folder CreateCopy()
|
||||||
{
|
{
|
||||||
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
|
JsonSerializerSettings settings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
|
||||||
FolderEntity entityCopy = JsonConvert.DeserializeObject<FolderEntity>(JsonConvert.SerializeObject(FolderEntity, settings), settings)!;
|
FolderEntity entityCopy = JsonConvert.DeserializeObject<FolderEntity>(JsonConvert.SerializeObject(FolderEntity, settings), settings)!;
|
||||||
entityCopy.Id = Guid.NewGuid();
|
entityCopy.Id = Guid.NewGuid();
|
||||||
entityCopy.Name += " - Copy";
|
entityCopy.Name += " - Copy";
|
||||||
@ -233,7 +229,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
lock (Timeline)
|
lock (Timeline)
|
||||||
{
|
{
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
{
|
{
|
||||||
baseLayerEffect.BaseProperties?.Update(Timeline);
|
baseLayerEffect.BaseProperties?.Update(Timeline);
|
||||||
@ -244,18 +239,18 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
canvas.Save();
|
canvas.Save();
|
||||||
Renderer.Open(Path, Parent as Folder);
|
Renderer.Open(Path, Parent as Folder);
|
||||||
|
|
||||||
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
|
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
|
||||||
throw new ArtemisCoreException("Failed to open folder render context");
|
throw new ArtemisCoreException("Failed to open folder render context");
|
||||||
|
|
||||||
|
SKRect rendererBounds = Renderer.Path.Bounds;
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PreProcess(Renderer.Canvas, Renderer.Path, Renderer.Paint);
|
baseLayerEffect.PreProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
||||||
|
|
||||||
// If required, apply the opacity override of the module to the root folder
|
// If required, apply the opacity override of the module to the root folder
|
||||||
if (IsRootFolder && Profile.Module.OpacityOverride < 1)
|
if (IsRootFolder && Profile.Module.OpacityOverride < 1)
|
||||||
{
|
{
|
||||||
double multiplier = Easings.SineEaseInOut(Profile.Module.OpacityOverride);
|
double multiplier = Easings.SineEaseInOut(Profile.Module.OpacityOverride);
|
||||||
Renderer.Paint.Color = Renderer.Paint.Color.WithAlpha((byte)(Renderer.Paint.Color.Alpha * multiplier));
|
Renderer.Paint.Color = Renderer.Paint.Color.WithAlpha((byte) (Renderer.Paint.Color.Alpha * multiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
// No point rendering if the alpha was set to zero by one of the effects
|
// No point rendering if the alpha was set to zero by one of the effects
|
||||||
@ -267,7 +262,7 @@ namespace Artemis.Core
|
|||||||
Children[index].Render(Renderer.Canvas);
|
Children[index].Render(Renderer.Canvas);
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PostProcess(Renderer.Canvas, Renderer.Path, Renderer.Paint);
|
baseLayerEffect.PostProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
||||||
|
|
||||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,6 @@ namespace Artemis.Core
|
|||||||
Enabled = true;
|
Enabled = true;
|
||||||
General = new LayerGeneralProperties();
|
General = new LayerGeneralProperties();
|
||||||
Transform = new LayerTransformProperties();
|
Transform = new LayerTransformProperties();
|
||||||
Renderer = new Renderer();
|
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
@ -58,7 +57,6 @@ namespace Artemis.Core
|
|||||||
Parent = parent;
|
Parent = parent;
|
||||||
General = new LayerGeneralProperties();
|
General = new LayerGeneralProperties();
|
||||||
Transform = new LayerTransformProperties();
|
Transform = new LayerTransformProperties();
|
||||||
Renderer = new Renderer();
|
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
@ -114,8 +112,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal override RenderElementEntity RenderElementEntity => LayerEntity;
|
internal override RenderElementEntity RenderElementEntity => LayerEntity;
|
||||||
|
|
||||||
internal Renderer Renderer { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a deep copy of the layer
|
/// Creates a deep copy of the layer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -349,11 +345,11 @@ namespace Artemis.Core
|
|||||||
Renderer.Paint.BlendMode = General.BlendMode.CurrentValue;
|
Renderer.Paint.BlendMode = General.BlendMode.CurrentValue;
|
||||||
Renderer.Paint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
|
Renderer.Paint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
|
||||||
|
|
||||||
// Clip anything outside the LED selection bounds
|
|
||||||
canvas.ClipPath(Renderer.ClipPath);
|
|
||||||
|
|
||||||
using SKPath renderPath = new SKPath();
|
using SKPath renderPath = new SKPath();
|
||||||
renderPath.AddRect(Renderer.Path.Bounds);
|
if (General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
|
||||||
|
renderPath.AddRect(Renderer.Path.Bounds);
|
||||||
|
else
|
||||||
|
renderPath.AddOval(Renderer.Path.Bounds);
|
||||||
|
|
||||||
if (General.TransformMode.CurrentValue == LayerTransformMode.Normal)
|
if (General.TransformMode.CurrentValue == LayerTransformMode.Normal)
|
||||||
{
|
{
|
||||||
@ -373,7 +369,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
||||||
Renderer.Canvas.ClipPath(renderPath);
|
Renderer.Canvas.ClipPath(renderPath);
|
||||||
DelegateRendering(renderPath);
|
DelegateRendering(renderPath.Bounds);
|
||||||
}
|
}
|
||||||
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
|
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
|
||||||
{
|
{
|
||||||
@ -382,7 +378,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
||||||
Renderer.Canvas.ClipPath(renderPath);
|
Renderer.Canvas.ClipPath(renderPath);
|
||||||
DelegateRendering(Renderer.Path);
|
DelegateRendering(Renderer.Path.Bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
||||||
@ -394,15 +390,15 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DelegateRendering(SKPath path)
|
private void DelegateRendering(SKRect bounds)
|
||||||
{
|
{
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PreProcess(Renderer.Canvas, path, Renderer.Paint);
|
baseLayerEffect.PreProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
||||||
|
|
||||||
LayerBrush.InternalRender(Renderer.Canvas, path, Renderer.Paint);
|
LayerBrush.InternalRender(Renderer.Canvas, bounds, Renderer.Paint);
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PostProcess(Renderer.Canvas, path, Renderer.Paint);
|
baseLayerEffect.PostProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CalculateRenderProperties()
|
internal void CalculateRenderProperties()
|
||||||
|
|||||||
@ -16,6 +16,7 @@ namespace Artemis.Core
|
|||||||
protected RenderProfileElement()
|
protected RenderProfileElement()
|
||||||
{
|
{
|
||||||
Timeline = new Timeline();
|
Timeline = new Timeline();
|
||||||
|
Renderer = new Renderer();
|
||||||
|
|
||||||
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||||
@ -102,9 +103,23 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
|
private ProfileElement _parent;
|
||||||
private SKPath _path;
|
private SKPath _path;
|
||||||
internal abstract RenderElementEntity RenderElementEntity { get; }
|
internal abstract RenderElementEntity RenderElementEntity { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parent of this element
|
||||||
|
/// </summary>
|
||||||
|
public new ProfileElement Parent
|
||||||
|
{
|
||||||
|
get => _parent;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
SetAndNotify(ref _parent, value);
|
||||||
|
Renderer.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
|
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
|
||||||
/// clipped.
|
/// clipped.
|
||||||
@ -118,6 +133,7 @@ namespace Artemis.Core
|
|||||||
// I can't really be sure about the performance impact of calling Bounds often but
|
// I can't really be sure about the performance impact of calling Bounds often but
|
||||||
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
|
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
|
||||||
Bounds = value?.Bounds ?? SKRect.Empty;
|
Bounds = value?.Bounds ?? SKRect.Empty;
|
||||||
|
Renderer.Invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +146,8 @@ namespace Artemis.Core
|
|||||||
private set => SetAndNotify(ref _bounds, value);
|
private set => SetAndNotify(ref _bounds, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal Renderer Renderer { get; }
|
||||||
|
|
||||||
#region Property group expansion
|
#region Property group expansion
|
||||||
|
|
||||||
protected List<string> _expandedPropertyGroups;
|
protected List<string> _expandedPropertyGroups;
|
||||||
|
|||||||
@ -5,12 +5,12 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal class Renderer : IDisposable
|
internal class Renderer : IDisposable
|
||||||
{
|
{
|
||||||
|
private bool _valid;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
public SKBitmap? Bitmap { get; private set; }
|
public SKBitmap? Bitmap { get; private set; }
|
||||||
public SKCanvas? Canvas { get; private set; }
|
public SKCanvas? Canvas { get; private set; }
|
||||||
public SKPaint? Paint { get; private set; }
|
public SKPaint? Paint { get; private set; }
|
||||||
public SKPath? Path { get; private set; }
|
public SKPath? Path { get; private set; }
|
||||||
public SKPath? ClipPath { get; private set; }
|
|
||||||
public SKPoint TargetLocation { get; private set; }
|
public SKPoint TargetLocation { get; private set; }
|
||||||
|
|
||||||
public bool IsOpen { get; private set; }
|
public bool IsOpen { get; private set; }
|
||||||
@ -26,27 +26,30 @@ namespace Artemis.Core
|
|||||||
if (IsOpen)
|
if (IsOpen)
|
||||||
throw new ArtemisCoreException("Cannot open render context because it is already open");
|
throw new ArtemisCoreException("Cannot open render context because it is already open");
|
||||||
|
|
||||||
int width = (int) path.Bounds.Width;
|
if (!_valid)
|
||||||
int height = (int) path.Bounds.Height;
|
{
|
||||||
|
SKRect pathBounds = path.Bounds;
|
||||||
|
int width = (int) pathBounds.Width;
|
||||||
|
int height = (int) pathBounds.Height;
|
||||||
|
|
||||||
if (Bitmap == null)
|
|
||||||
Bitmap = new SKBitmap(width, height);
|
|
||||||
else if (Bitmap.Info.Width != width || Bitmap.Info.Height != height)
|
|
||||||
Bitmap = new SKBitmap(width, height);
|
Bitmap = new SKBitmap(width, height);
|
||||||
|
Path = new SKPath(path);
|
||||||
|
Canvas = new SKCanvas(Bitmap);
|
||||||
|
Path.Transform(SKMatrix.MakeTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
|
||||||
|
|
||||||
|
TargetLocation = new SKPoint(pathBounds.Location.X, pathBounds.Location.Y);
|
||||||
|
if (parent != null)
|
||||||
|
TargetLocation -= parent.Bounds.Location;
|
||||||
|
|
||||||
|
Canvas.ClipPath(Path);
|
||||||
|
|
||||||
|
_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
Path = new SKPath(path);
|
|
||||||
Canvas = new SKCanvas(Bitmap);
|
|
||||||
Paint = new SKPaint();
|
Paint = new SKPaint();
|
||||||
|
|
||||||
Path.Transform(SKMatrix.MakeTranslation(Path.Bounds.Left * -1, Path.Bounds.Top * -1));
|
|
||||||
Canvas.Clear();
|
Canvas.Clear();
|
||||||
|
Canvas.Save();
|
||||||
TargetLocation = new SKPoint(path.Bounds.Location.X, path.Bounds.Location.Y);
|
|
||||||
if (parent != null)
|
|
||||||
TargetLocation -= parent.Path.Bounds.Location;
|
|
||||||
|
|
||||||
ClipPath = new SKPath(Path);
|
|
||||||
ClipPath.Transform(SKMatrix.MakeTranslation(TargetLocation.X, TargetLocation.Y));
|
|
||||||
|
|
||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
}
|
}
|
||||||
@ -56,25 +59,34 @@ namespace Artemis.Core
|
|||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("Renderer");
|
throw new ObjectDisposedException("Renderer");
|
||||||
|
|
||||||
Canvas?.Dispose();
|
Canvas.Restore();
|
||||||
Paint?.Dispose();
|
Paint?.Dispose();
|
||||||
Path?.Dispose();
|
|
||||||
ClipPath?.Dispose();
|
|
||||||
|
|
||||||
Canvas = null;
|
|
||||||
Paint = null;
|
Paint = null;
|
||||||
Path = null;
|
|
||||||
ClipPath = null;
|
|
||||||
|
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Invalidate()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("Renderer");
|
||||||
|
|
||||||
|
_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (IsOpen)
|
if (IsOpen)
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
|
Canvas?.Dispose();
|
||||||
|
Paint?.Dispose();
|
||||||
|
Path?.Dispose();
|
||||||
Bitmap?.Dispose();
|
Bitmap?.Dispose();
|
||||||
|
|
||||||
|
Canvas = null;
|
||||||
|
Paint = null;
|
||||||
|
Path = null;
|
||||||
Bitmap = null;
|
Bitmap = null;
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
@ -95,7 +95,7 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
// but LayerBrush<T> and RgbNetLayerBrush<T> outside the core
|
// but LayerBrush<T> and RgbNetLayerBrush<T> outside the core
|
||||||
internal abstract void Initialize();
|
internal abstract void Initialize();
|
||||||
|
|
||||||
internal abstract void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint);
|
internal abstract void InternalRender(SKCanvas canvas, SKRect path, SKPaint paint);
|
||||||
|
|
||||||
internal virtual void Dispose(bool disposing)
|
internal virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -22,13 +22,13 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// <para>Called during rendering or layer preview, in the order configured on the layer</para>
|
/// <para>Called during rendering or layer preview, in the order configured on the layer</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="canvas">The layer canvas</param>
|
/// <param name="canvas">The layer canvas</param>
|
||||||
/// <param name="path">The path to be filled, represents the shape</param>
|
/// <param name="bounds">The area to be filled, covers the shape</param>
|
||||||
/// <param name="paint">The paint to be used to fill the shape</param>
|
/// <param name="paint">The paint to be used to fill the shape</param>
|
||||||
public abstract void Render(SKCanvas canvas, SKPath path, SKPaint paint);
|
public abstract void Render(SKCanvas canvas, SKRect bounds, SKPaint paint);
|
||||||
|
|
||||||
internal override void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint)
|
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
||||||
{
|
{
|
||||||
Render(canvas, path, paint);
|
Render(canvas, bounds, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize()
|
internal override void Initialize()
|
||||||
|
|||||||
@ -28,7 +28,7 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// <returns>The color the LED will receive</returns>
|
/// <returns>The color the LED will receive</returns>
|
||||||
public abstract SKColor GetColor(ArtemisLed led, SKPoint renderPoint);
|
public abstract SKColor GetColor(ArtemisLed led, SKPoint renderPoint);
|
||||||
|
|
||||||
internal override void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint)
|
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
||||||
{
|
{
|
||||||
// We don't want rotation on this canvas because that'll displace the LEDs, translations are applied to the points of each LED instead
|
// We don't want rotation on this canvas because that'll displace the LEDs, translations are applied to the points of each LED instead
|
||||||
if (Layer.General.TransformMode.CurrentValue == LayerTransformMode.Normal && SupportsTransformation)
|
if (Layer.General.TransformMode.CurrentValue == LayerTransformMode.Normal && SupportsTransformation)
|
||||||
|
|||||||
@ -68,7 +68,7 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not used in this effect type
|
// Not used in this effect type
|
||||||
internal override void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint)
|
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("RGB.NET layer effectes do not implement InternalRender");
|
throw new NotImplementedException("RGB.NET layer effectes do not implement InternalRender");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,7 +132,7 @@ namespace Artemis.Core.LayerEffects
|
|||||||
/// <param name="canvas">The canvas used to render the frame</param>
|
/// <param name="canvas">The canvas used to render the frame</param>
|
||||||
/// <param name="renderBounds">The bounds this layer/folder will render in</param>
|
/// <param name="renderBounds">The bounds this layer/folder will render in</param>
|
||||||
/// <param name="paint">The paint this layer/folder will use to render</param>
|
/// <param name="paint">The paint this layer/folder will use to render</param>
|
||||||
public abstract void PreProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint);
|
public abstract void PreProcess(SKCanvas canvas, SKRect renderBounds, SKPaint paint);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called after the layer of folder has been rendered
|
/// Called after the layer of folder has been rendered
|
||||||
@ -140,7 +140,7 @@ namespace Artemis.Core.LayerEffects
|
|||||||
/// <param name="canvas">The canvas used to render the frame</param>
|
/// <param name="canvas">The canvas used to render the frame</param>
|
||||||
/// <param name="renderBounds">The bounds this layer/folder rendered in</param>
|
/// <param name="renderBounds">The bounds this layer/folder rendered in</param>
|
||||||
/// <param name="paint">The paint this layer/folder used to render</param>
|
/// <param name="paint">The paint this layer/folder used to render</param>
|
||||||
public abstract void PostProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint);
|
public abstract void PostProcess(SKCanvas canvas, SKRect renderBounds, SKPaint paint);
|
||||||
|
|
||||||
// Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything
|
// Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything
|
||||||
// but LayerEffect<T> outside the core
|
// but LayerEffect<T> outside the core
|
||||||
|
|||||||
@ -40,12 +40,12 @@ namespace Artemis.Core.LayerEffects.Placeholder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void PreProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint)
|
public override void PreProcess(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void PostProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint)
|
public override void PostProcess(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -124,17 +124,26 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
public List<Plugin> GetAllPlugins()
|
public List<Plugin> GetAllPlugins()
|
||||||
{
|
{
|
||||||
return new List<Plugin>(_plugins);
|
lock (_plugins)
|
||||||
|
{
|
||||||
|
return new List<Plugin>(_plugins);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<T> GetFeaturesOfType<T>() where T : PluginFeature
|
public List<T> GetFeaturesOfType<T>() where T : PluginFeature
|
||||||
{
|
{
|
||||||
return _plugins.Where(p => p.IsEnabled).SelectMany(p => p.Features.Where(i => i.IsEnabled && i is T)).Cast<T>().ToList();
|
lock (_plugins)
|
||||||
|
{
|
||||||
|
return _plugins.Where(p => p.IsEnabled).SelectMany(p => p.Features.Where(i => i.IsEnabled && i is T)).Cast<T>().ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plugin? GetPluginByAssembly(Assembly assembly)
|
public Plugin? GetPluginByAssembly(Assembly assembly)
|
||||||
{
|
{
|
||||||
return _plugins.FirstOrDefault(p => p.Assembly == assembly);
|
lock (_plugins)
|
||||||
|
{
|
||||||
|
return _plugins.FirstOrDefault(p => p.Assembly == assembly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to a more appropriate service
|
// TODO: move to a more appropriate service
|
||||||
@ -171,105 +180,104 @@ namespace Artemis.Core.Services
|
|||||||
if (LoadingPlugins)
|
if (LoadingPlugins)
|
||||||
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
|
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
|
||||||
|
|
||||||
lock (_plugins)
|
LoadingPlugins = true;
|
||||||
|
|
||||||
|
// Unload all currently loaded plugins first
|
||||||
|
UnloadPlugins();
|
||||||
|
|
||||||
|
// Load the plugin assemblies into the plugin context
|
||||||
|
DirectoryInfo pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins"));
|
||||||
|
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
||||||
{
|
{
|
||||||
LoadingPlugins = true;
|
try
|
||||||
|
|
||||||
// Unload all currently loaded plugins first
|
|
||||||
UnloadPlugins();
|
|
||||||
|
|
||||||
// Load the plugin assemblies into the plugin context
|
|
||||||
DirectoryInfo pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins"));
|
|
||||||
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
|
||||||
{
|
{
|
||||||
try
|
Plugin plugin = LoadPlugin(subDirectory);
|
||||||
{
|
if (plugin.Entity.IsEnabled)
|
||||||
Plugin plugin = LoadPlugin(subDirectory);
|
EnablePlugin(plugin, false, ignorePluginLock);
|
||||||
if (plugin.Entity.IsEnabled)
|
}
|
||||||
EnablePlugin(plugin, false, ignorePluginLock);
|
catch (Exception e)
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
||||||
{
|
|
||||||
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadingPlugins = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadingPlugins = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnloadPlugins()
|
public void UnloadPlugins()
|
||||||
{
|
{
|
||||||
|
// Unload all plugins
|
||||||
|
while (_plugins.Count > 0)
|
||||||
|
UnloadPlugin(_plugins[0]);
|
||||||
|
|
||||||
lock (_plugins)
|
lock (_plugins)
|
||||||
{
|
{
|
||||||
// Unload all plugins
|
|
||||||
while (_plugins.Count > 0)
|
|
||||||
UnloadPlugin(_plugins[0]);
|
|
||||||
|
|
||||||
_plugins.Clear();
|
_plugins.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plugin LoadPlugin(DirectoryInfo directory)
|
public Plugin LoadPlugin(DirectoryInfo directory)
|
||||||
{
|
{
|
||||||
|
_logger.Debug("Loading plugin from {directory}", directory.FullName);
|
||||||
|
|
||||||
|
// Load the metadata
|
||||||
|
string metadataFile = Path.Combine(directory.FullName, "plugin.json");
|
||||||
|
if (!File.Exists(metadataFile))
|
||||||
|
_logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception");
|
||||||
|
|
||||||
|
// PluginInfo contains the ID which we need to move on
|
||||||
|
PluginInfo pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
|
||||||
|
|
||||||
|
if (pluginInfo.Guid == Constants.CorePluginInfo.Guid)
|
||||||
|
throw new ArtemisPluginException($"Plugin cannot use reserved GUID {pluginInfo.Guid}");
|
||||||
|
|
||||||
lock (_plugins)
|
lock (_plugins)
|
||||||
{
|
{
|
||||||
_logger.Debug("Loading plugin from {directory}", directory.FullName);
|
|
||||||
|
|
||||||
// Load the metadata
|
|
||||||
string metadataFile = Path.Combine(directory.FullName, "plugin.json");
|
|
||||||
if (!File.Exists(metadataFile))
|
|
||||||
_logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception");
|
|
||||||
|
|
||||||
// PluginInfo contains the ID which we need to move on
|
|
||||||
PluginInfo pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
|
|
||||||
|
|
||||||
if (pluginInfo.Guid == Constants.CorePluginInfo.Guid)
|
|
||||||
throw new ArtemisPluginException($"Plugin cannot use reserved GUID {pluginInfo.Guid}");
|
|
||||||
|
|
||||||
// Ensure the plugin is not already loaded
|
// Ensure the plugin is not already loaded
|
||||||
if (_plugins.Any(p => p.Guid == pluginInfo.Guid))
|
if (_plugins.Any(p => p.Guid == pluginInfo.Guid))
|
||||||
throw new ArtemisCoreException("Cannot load a plugin that is already loaded");
|
throw new ArtemisCoreException("Cannot load a plugin that is already loaded");
|
||||||
|
|
||||||
Plugin plugin = new Plugin(pluginInfo, directory);
|
|
||||||
OnPluginLoading(new PluginEventArgs(plugin));
|
|
||||||
|
|
||||||
// Load the entity and fall back on creating a new one
|
|
||||||
plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity {Id = plugin.Guid, IsEnabled = true};
|
|
||||||
|
|
||||||
// Locate the main assembly entry
|
|
||||||
string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main);
|
|
||||||
if (!File.Exists(mainFile))
|
|
||||||
throw new ArtemisPluginException(plugin, "Couldn't find the plugins main entry at " + mainFile);
|
|
||||||
|
|
||||||
// Load the plugin, all types implementing Plugin and register them with DI
|
|
||||||
plugin.PluginLoader = PluginLoader.CreateFromAssemblyFile(mainFile!, configure =>
|
|
||||||
{
|
|
||||||
configure.IsUnloadable = true;
|
|
||||||
configure.PreferSharedTypes = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
plugin.Assembly = plugin.PluginLoader.LoadDefaultAssembly();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
|
|
||||||
if (bootstrappers.Count > 1)
|
|
||||||
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
|
||||||
if (bootstrappers.Any())
|
|
||||||
plugin.Bootstrapper = (IPluginBootstrapper?) Activator.CreateInstance(bootstrappers.First());
|
|
||||||
|
|
||||||
_plugins.Add(plugin);
|
|
||||||
|
|
||||||
OnPluginLoaded(new PluginEventArgs(plugin));
|
|
||||||
|
|
||||||
return plugin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Plugin plugin = new Plugin(pluginInfo, directory);
|
||||||
|
OnPluginLoading(new PluginEventArgs(plugin));
|
||||||
|
|
||||||
|
// Load the entity and fall back on creating a new one
|
||||||
|
plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity {Id = plugin.Guid, IsEnabled = true};
|
||||||
|
|
||||||
|
// Locate the main assembly entry
|
||||||
|
string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main);
|
||||||
|
if (!File.Exists(mainFile))
|
||||||
|
throw new ArtemisPluginException(plugin, "Couldn't find the plugins main entry at " + mainFile);
|
||||||
|
|
||||||
|
// Load the plugin, all types implementing Plugin and register them with DI
|
||||||
|
plugin.PluginLoader = PluginLoader.CreateFromAssemblyFile(mainFile!, configure =>
|
||||||
|
{
|
||||||
|
configure.IsUnloadable = true;
|
||||||
|
configure.PreferSharedTypes = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
plugin.Assembly = plugin.PluginLoader.LoadDefaultAssembly();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
|
||||||
|
if (bootstrappers.Count > 1)
|
||||||
|
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
||||||
|
if (bootstrappers.Any())
|
||||||
|
plugin.Bootstrapper = (IPluginBootstrapper?) Activator.CreateInstance(bootstrappers.First());
|
||||||
|
|
||||||
|
lock (_plugins)
|
||||||
|
{
|
||||||
|
_plugins.Add(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPluginLoaded(new PluginEventArgs(plugin));
|
||||||
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock)
|
public void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock)
|
||||||
@ -342,22 +350,22 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
public void UnloadPlugin(Plugin plugin)
|
public void UnloadPlugin(Plugin plugin)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DisablePlugin(plugin, false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Warning(new ArtemisPluginException(plugin, "Exception during DisablePlugin call for UnloadPlugin", e), "Failed to unload plugin");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
OnPluginDisabled(new PluginEventArgs(plugin));
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.Dispose();
|
||||||
lock (_plugins)
|
lock (_plugins)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
DisablePlugin(plugin, false);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.Warning(new ArtemisPluginException(plugin, "Exception during DisablePlugin call for UnloadPlugin", e), "Failed to unload plugin");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
OnPluginDisabled(new PluginEventArgs(plugin));
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.Dispose();
|
|
||||||
_plugins.Remove(plugin);
|
_plugins.Remove(plugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,70 +409,65 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
_logger.Debug("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
_logger.Debug("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
||||||
|
|
||||||
lock (_plugins)
|
|
||||||
|
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
|
||||||
|
try
|
||||||
{
|
{
|
||||||
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
|
pluginFeature.SetEnabled(true, isAutoEnable);
|
||||||
try
|
if (saveState)
|
||||||
|
pluginFeature.Entity.IsEnabled = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Warning(
|
||||||
|
new ArtemisPluginException(pluginFeature.Plugin, $"Exception during SetEnabled(true) on {pluginFeature}", e),
|
||||||
|
"Failed to enable plugin"
|
||||||
|
);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// On an auto-enable, ensure PluginInfo.Enabled is true even if enable failed, that way a failure on auto-enable does
|
||||||
|
// not affect the user's settings
|
||||||
|
if (saveState)
|
||||||
{
|
{
|
||||||
pluginFeature.SetEnabled(true, isAutoEnable);
|
if (isAutoEnable)
|
||||||
if (saveState)
|
|
||||||
pluginFeature.Entity.IsEnabled = true;
|
pluginFeature.Entity.IsEnabled = true;
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.Warning(
|
|
||||||
new ArtemisPluginException(pluginFeature.Plugin, $"Exception during SetEnabled(true) on {pluginFeature}", e),
|
|
||||||
"Failed to enable plugin"
|
|
||||||
);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// On an auto-enable, ensure PluginInfo.Enabled is true even if enable failed, that way a failure on auto-enable does
|
|
||||||
// not affect the user's settings
|
|
||||||
if (saveState)
|
|
||||||
{
|
|
||||||
if (isAutoEnable)
|
|
||||||
pluginFeature.Entity.IsEnabled = true;
|
|
||||||
|
|
||||||
SavePlugin(pluginFeature.Plugin);
|
SavePlugin(pluginFeature.Plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginFeature.IsEnabled)
|
if (pluginFeature.IsEnabled)
|
||||||
{
|
{
|
||||||
_logger.Debug("Successfully enabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
_logger.Debug("Successfully enabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
||||||
OnPluginFeatureEnabled(new PluginFeatureEventArgs(pluginFeature));
|
OnPluginFeatureEnabled(new PluginFeatureEventArgs(pluginFeature));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OnPluginFeatureEnableFailed(new PluginFeatureEventArgs(pluginFeature));
|
OnPluginFeatureEnableFailed(new PluginFeatureEventArgs(pluginFeature));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisablePluginFeature(PluginFeature pluginFeature, bool saveState)
|
public void DisablePluginFeature(PluginFeature pluginFeature, bool saveState)
|
||||||
{
|
{
|
||||||
lock (_plugins)
|
try
|
||||||
{
|
{
|
||||||
try
|
_logger.Debug("Disabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
||||||
|
pluginFeature.SetEnabled(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (saveState)
|
||||||
{
|
{
|
||||||
_logger.Debug("Disabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
pluginFeature.Entity.IsEnabled = false;
|
||||||
pluginFeature.SetEnabled(false);
|
SavePlugin(pluginFeature.Plugin);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (saveState)
|
|
||||||
{
|
|
||||||
pluginFeature.Entity.IsEnabled = false;
|
|
||||||
SavePlugin(pluginFeature.Plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pluginFeature.IsEnabled)
|
if (!pluginFeature.IsEnabled)
|
||||||
{
|
{
|
||||||
_logger.Debug("Successfully disabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
_logger.Debug("Successfully disabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
||||||
OnPluginFeatureDisabled(new PluginFeatureEventArgs(pluginFeature));
|
OnPluginFeatureDisabled(new PluginFeatureEventArgs(pluginFeature));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user