Profiles - Tweaked render pipeline, improving render performance by ~40%

This commit is contained in:
SpoinkyNL committed 2020-11-15 17:38:06 +01:00
1 parent 7735edea1e
commit 991c5fd955
11 files changed
+231 -207

No files matched your search

+7 -12
View File
@@ -29,7 +29,6 @@ namespace Artemis.Core
Profile = Parent.Profile;
Name = name;
Enabled = true;
Renderer = new Renderer();
_layerEffects = new List<BaseLayerEffect>();
_expandedPropertyGroups = new List<string>();
@@ -47,7 +46,6 @@ namespace Artemis.Core
Name = folderEntity.Name;
Enabled = folderEntity.Enabled;
Order = folderEntity.Order;
Renderer = new Renderer();
_layerEffects = new List<BaseLayerEffect>();
_expandedPropertyGroups = new List<string>();
@@ -69,8 +67,6 @@ namespace Artemis.Core
internal override RenderElementEntity RenderElementEntity => FolderEntity;
internal Renderer Renderer { get; }
/// <inheritdoc />
public override List<ILayerProperty> GetAllLayerProperties()
{
@@ -133,7 +129,7 @@ namespace Artemis.Core
/// <returns>The newly created copy</returns>
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)!;
entityCopy.Id = Guid.NewGuid();
entityCopy.Name += " - Copy";
@@ -233,29 +229,28 @@ namespace Artemis.Core
lock (Timeline)
{
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
{
baseLayerEffect.BaseProperties?.Update(Timeline);
baseLayerEffect.Update(Timeline.Delta.TotalSeconds);
}
try
{
canvas.Save();
Renderer.Open(Path, Parent as Folder);
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
throw new ArtemisCoreException("Failed to open folder render context");
SKRect rendererBounds = Renderer.Path.Bounds;
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 (IsRootFolder && Profile.Module.OpacityOverride < 1)
{
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
@@ -263,11 +258,11 @@ namespace Artemis.Core
return;
// Iterate the children in reverse because the first layer must be rendered last to end up on top
for (int index = Children.Count - 1; index > -1; index--)
for (int index = Children.Count - 1; index > -1; index--)
Children[index].Render(Renderer.Canvas);
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);
}
+12 -16
View File
@@ -39,7 +39,6 @@ namespace Artemis.Core
Enabled = true;
General = new LayerGeneralProperties();
Transform = new LayerTransformProperties();
Renderer = new Renderer();
_layerEffects = new List<BaseLayerEffect>();
_leds = new List<ArtemisLed>();
@@ -58,8 +57,7 @@ namespace Artemis.Core
Parent = parent;
General = new LayerGeneralProperties();
Transform = new LayerTransformProperties();
Renderer = new Renderer();
_layerEffects = new List<BaseLayerEffect>();
_leds = new List<ArtemisLed>();
_expandedPropertyGroups = new List<string>();
@@ -113,9 +111,7 @@ namespace Artemis.Core
internal LayerEntity LayerEntity { get; set; }
internal override RenderElementEntity RenderElementEntity => LayerEntity;
internal Renderer Renderer { get; }
/// <summary>
/// Creates a deep copy of the layer
/// </summary>
@@ -349,11 +345,11 @@ namespace Artemis.Core
Renderer.Paint.BlendMode = General.BlendMode.CurrentValue;
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();
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)
{
@@ -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
Renderer.Canvas.ClipPath(renderPath);
DelegateRendering(renderPath);
DelegateRendering(renderPath.Bounds);
}
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
Renderer.Canvas.ClipPath(renderPath);
DelegateRendering(Renderer.Path);
DelegateRendering(Renderer.Path.Bounds);
}
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))
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))
baseLayerEffect.PostProcess(Renderer.Canvas, path, Renderer.Paint);
baseLayerEffect.PostProcess(Renderer.Canvas, bounds, Renderer.Paint);
}
internal void CalculateRenderProperties()
@@ -16,6 +16,7 @@ namespace Artemis.Core
protected RenderProfileElement()
{
Timeline = new Timeline();
Renderer = new Renderer();
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
@@ -102,9 +103,23 @@ namespace Artemis.Core
#region Properties
private ProfileElement _parent;
private SKPath _path;
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>
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
/// clipped.
@@ -118,6 +133,7 @@ namespace Artemis.Core
// 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
Bounds = value?.Bounds ?? SKRect.Empty;
Renderer.Invalidate();
}
}
@@ -130,6 +146,8 @@ namespace Artemis.Core
private set => SetAndNotify(ref _bounds, value);
}
internal Renderer Renderer { get; }
#region Property group expansion
protected List<string> _expandedPropertyGroups;
+36 -24
View File
@@ -5,12 +5,12 @@ namespace Artemis.Core
{
internal class Renderer : IDisposable
{
private bool _valid;
private bool _disposed;
public SKBitmap? Bitmap { get; private set; }
public SKCanvas? Canvas { get; private set; }
public SKPaint? Paint { get; private set; }
public SKPath? Path { get; private set; }
public SKPath? ClipPath { get; private set; }
public SKPoint TargetLocation { get; private set; }
public bool IsOpen { get; private set; }
@@ -26,27 +26,30 @@ namespace Artemis.Core
if (IsOpen)
throw new ArtemisCoreException("Cannot open render context because it is already open");
int width = (int) path.Bounds.Width;
int height = (int) path.Bounds.Height;
if (!_valid)
{
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);
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();
Path.Transform(SKMatrix.MakeTranslation(Path.Bounds.Left * -1, Path.Bounds.Top * -1));
Canvas.Clear();
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));
Canvas.Save();
IsOpen = true;
}
@@ -55,26 +58,35 @@ namespace Artemis.Core
{
if (_disposed)
throw new ObjectDisposedException("Renderer");
Canvas?.Dispose();
Canvas.Restore();
Paint?.Dispose();
Path?.Dispose();
ClipPath?.Dispose();
Canvas = null;
Paint = null;
Path = null;
ClipPath = null;
IsOpen = false;
}
public void Invalidate()
{
if (_disposed)
throw new ObjectDisposedException("Renderer");
_valid = false;
}
public void Dispose()
{
if (IsOpen)
Close();
Canvas?.Dispose();
Paint?.Dispose();
Path?.Dispose();
Bitmap?.Dispose();
Canvas = null;
Paint = null;
Path = null;
Bitmap = null;
_disposed = true;