1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Rendering - Improved sampling performance

Plugins - Added update/render time measuring
Settings - Added a way to enable 60/144 FPS 🕵🏻‍♂️
This commit is contained in:
Robert 2021-03-22 21:12:58 +01:00
parent fc9476247f
commit 97668ee932
8 changed files with 141 additions and 75 deletions

View File

@ -318,7 +318,7 @@ namespace Artemis.Core
if (Path == null || LayerBrush == null) if (Path == null || LayerBrush == null)
throw new ArtemisCoreException("The layer is not yet ready for rendering"); throw new ArtemisCoreException("The layer is not yet ready for rendering");
if (timeline.IsFinished) if (!Leds.Any() || timeline.IsFinished)
return; return;
ApplyTimeline(timeline); ApplyTimeline(timeline);

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using Artemis.Core.DataModelExpansions; using Artemis.Core.DataModelExpansions;
using Artemis.Storage.Entities.Module; using Artemis.Storage.Entities.Module;
@ -186,13 +187,17 @@ namespace Artemis.Core.Modules
internal virtual void InternalUpdate(double deltaTime) internal virtual void InternalUpdate(double deltaTime)
{ {
StartUpdateMeasure();
if (IsUpdateAllowed) if (IsUpdateAllowed)
Update(deltaTime); Update(deltaTime);
StopUpdateMeasure();
} }
internal virtual void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) internal virtual void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{ {
StartRenderMeasure();
Render(deltaTime, canvas, canvasInfo); Render(deltaTime, canvas, canvasInfo);
StopRenderMeasure();
} }
internal virtual void Activate(bool isOverride) internal virtual void Activate(bool isOverride)

View File

@ -150,6 +150,7 @@ namespace Artemis.Core.Modules
internal override void InternalUpdate(double deltaTime) internal override void InternalUpdate(double deltaTime)
{ {
StartUpdateMeasure();
if (IsUpdateAllowed) if (IsUpdateAllowed)
Update(deltaTime); Update(deltaTime);
@ -165,10 +166,12 @@ namespace Artemis.Core.Modules
} }
ProfileUpdated(deltaTime); ProfileUpdated(deltaTime);
StopUpdateMeasure();
} }
internal override void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) internal override void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{ {
StartRenderMeasure();
Render(deltaTime, canvas, canvasInfo); Render(deltaTime, canvas, canvasInfo);
lock (_lock) lock (_lock)
@ -178,6 +181,7 @@ namespace Artemis.Core.Modules
} }
ProfileRendered(deltaTime, canvas, canvasInfo); ProfileRendered(deltaTime, canvas, canvasInfo);
StopRenderMeasure();
} }
internal async Task ChangeActiveProfileAnimated(Profile? profile, IEnumerable<ArtemisDevice> devices) internal async Task ChangeActiveProfileAnimated(Profile? profile, IEnumerable<ArtemisDevice> devices)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
@ -10,9 +11,11 @@ namespace Artemis.Core
/// </summary> /// </summary>
public abstract class PluginFeature : CorePropertyChanged, IDisposable public abstract class PluginFeature : CorePropertyChanged, IDisposable
{ {
private readonly Stopwatch _renderStopwatch = new();
private readonly Stopwatch _updateStopwatch = new();
private bool _isEnabled; private bool _isEnabled;
private Exception? _loadException; private Exception? _loadException;
/// <summary> /// <summary>
/// Gets the plugin feature info related to this feature /// Gets the plugin feature info related to this feature
/// </summary> /// </summary>
@ -46,6 +49,16 @@ namespace Artemis.Core
/// </summary> /// </summary>
public string Id => $"{GetType().FullName}-{Plugin.Guid.ToString().Substring(0, 8)}"; // Not as unique as a GUID but good enough and stays readable public string Id => $"{GetType().FullName}-{Plugin.Guid.ToString().Substring(0, 8)}"; // Not as unique as a GUID but good enough and stays readable
/// <summary>
/// Gets the last measured update time of the feature
/// </summary>
public TimeSpan UpdateTime { get; private set; }
/// <summary>
/// Gets the last measured render time of the feature
/// </summary>
public TimeSpan RenderTime { get; private set; }
internal PluginFeatureEntity Entity { get; set; } = null!; // Will be set right after construction internal PluginFeatureEntity Entity { get; set; } = null!; // Will be set right after construction
/// <summary> /// <summary>
@ -58,6 +71,66 @@ namespace Artemis.Core
/// </summary> /// </summary>
public abstract void Disable(); public abstract void Disable();
/// <summary>
/// Occurs when the feature is enabled
/// </summary>
public event EventHandler? Enabled;
/// <summary>
/// Occurs when the feature is disabled
/// </summary>
public event EventHandler? Disabled;
/// <summary>
/// Releases the unmanaged resources used by the plugin feature and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing) InternalDisable();
}
/// <summary>
/// Triggers the Enabled event
/// </summary>
protected virtual void OnEnabled()
{
Enabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Triggers the Disabled event
/// </summary>
protected virtual void OnDisabled()
{
Disabled?.Invoke(this, EventArgs.Empty);
}
internal void StartUpdateMeasure()
{
_updateStopwatch.Start();
}
internal void StopUpdateMeasure()
{
UpdateTime = _updateStopwatch.Elapsed;
_updateStopwatch.Reset();
}
internal void StartRenderMeasure()
{
_renderStopwatch.Start();
}
internal void StopRenderMeasure()
{
RenderTime = _renderStopwatch.Elapsed;
_renderStopwatch.Reset();
}
internal void SetEnabled(bool enable, bool isAutoEnable = false) internal void SetEnabled(bool enable, bool isAutoEnable = false)
{ {
if (enable == IsEnabled) if (enable == IsEnabled)
@ -133,25 +206,6 @@ namespace Artemis.Core
Disable(); Disable();
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the plugin feature and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
InternalDisable();
}
}
#endregion
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
@ -187,35 +241,5 @@ namespace Artemis.Core
} }
#endregion #endregion
#region Events
/// <summary>
/// Occurs when the feature is enabled
/// </summary>
public event EventHandler? Enabled;
/// <summary>
/// Occurs when the feature is disabled
/// </summary>
public event EventHandler? Disabled;
/// <summary>
/// Triggers the Enabled event
/// </summary>
protected virtual void OnEnabled()
{
Enabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Triggers the Disabled event
/// </summary>
protected virtual void OnDisabled()
{
Disabled?.Invoke(this, EventArgs.Empty);
}
#endregion
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.InteropServices;
using Artemis.Core.SkiaSharp; using Artemis.Core.SkiaSharp;
using RGB.NET.Core; using RGB.NET.Core;
using RGB.NET.Presets.Textures.Sampler; using RGB.NET.Presets.Textures.Sampler;
@ -11,34 +12,39 @@ namespace Artemis.Core
/// </summary> /// </summary>
public sealed class SKTexture : PixelTexture<byte>, IDisposable public sealed class SKTexture : PixelTexture<byte>, IDisposable
{ {
private SKPixmap? _pixelData; private readonly SKPixmap _pixelData;
private SKImage? _rasterImage; private readonly IntPtr _pixelDataPtr;
#region Constructors #region Constructors
internal SKTexture(IManagedGraphicsContext? managedGraphicsContext, int width, int height, float renderScale) internal SKTexture(IManagedGraphicsContext? graphicsContext, int width, int height, float scale) : base(width, height, 4, new AverageByteSampler())
: base(width, height, 4, new AverageByteSampler())
{ {
ImageInfo = new SKImageInfo(width, height); ImageInfo = new SKImageInfo(width, height);
if (managedGraphicsContext == null) Surface = graphicsContext == null
Surface = SKSurface.Create(ImageInfo); ? SKSurface.Create(ImageInfo)
else : SKSurface.Create(graphicsContext.GraphicsContext, true, ImageInfo);
Surface = SKSurface.Create(managedGraphicsContext.GraphicsContext, true, ImageInfo); RenderScale = scale;
RenderScale = renderScale;
_pixelDataPtr = Marshal.AllocHGlobal(ImageInfo.BytesSize);
_pixelData = new SKPixmap(ImageInfo, _pixelDataPtr, ImageInfo.RowBytes);
} }
#endregion #endregion
#region Methods #region Methods
/// <summary>
/// Invalidates the texture
/// </summary>
public void Invalidate()
{
IsInvalid = true;
}
internal void CopyPixelData() internal void CopyPixelData()
{ {
using SKImage skImage = Surface.Snapshot(); using SKImage skImage = Surface.Snapshot();
skImage.ReadPixels(_pixelData);
_rasterImage?.Dispose();
_pixelData?.Dispose();
_rasterImage = skImage.ToRasterImage();
_pixelData = _rasterImage.PeekPixels();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -64,7 +70,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the color data in RGB format /// Gets the color data in RGB format
/// </summary> /// </summary>
protected override ReadOnlySpan<byte> Data => _pixelData != null ? _pixelData.GetPixelSpan() : ReadOnlySpan<byte>.Empty; protected override ReadOnlySpan<byte> Data => _pixelData.GetPixelSpan();
/// <summary> /// <summary>
/// Gets the render scale of the texture /// Gets the render scale of the texture
@ -77,19 +83,29 @@ namespace Artemis.Core
/// </summary> /// </summary>
public bool IsInvalid { get; private set; } public bool IsInvalid { get; private set; }
/// <summary> #endregion
/// Invalidates the texture
/// </summary> #region IDisposable
public void Invalidate()
private void ReleaseUnmanagedResources()
{ {
IsInvalid = true; Marshal.FreeHGlobal(_pixelDataPtr);
} }
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Surface.Dispose(); Surface.Dispose();
_pixelData?.Dispose(); _pixelData.Dispose();
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
/// <inheritdoc />
~SKTexture()
{
ReleaseUnmanagedResources();
} }
#endregion #endregion

View File

@ -35,6 +35,9 @@
<Run Text=" at " /> <Run Text=" at " />
<Run Text="{Binding RenderWidth}" /><Run Text="x" /> <Run Text="{Binding RenderWidth}" /><Run Text="x" />
<Run Text="{Binding RenderHeight}" /> <Run Text="{Binding RenderHeight}" />
<Run Text=" - Renderer: "></Run>
<Run Text="{Binding Renderer}"></Run>
</TextBlock> </TextBlock>
</Grid> </Grid>

View File

@ -20,6 +20,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
private int _renderWidth; private int _renderWidth;
private int _renderHeight; private int _renderHeight;
private string _frameTargetPath; private string _frameTargetPath;
private string _renderer;
public RenderDebugViewModel(ICoreService coreService) public RenderDebugViewModel(ICoreService coreService)
{ {
@ -51,6 +52,12 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
set => SetAndNotify(ref _renderHeight, value); set => SetAndNotify(ref _renderHeight, value);
} }
public string Renderer
{
get => _renderer;
set => SetAndNotify(ref _renderer, value);
}
public void SaveFrame() public void SaveFrame()
{ {
VistaSaveFileDialog dialog = new VistaSaveFileDialog {Filter = "Portable network graphic (*.png)|*.png", Title = "Save render frame"}; VistaSaveFileDialog dialog = new VistaSaveFileDialog {Filter = "Portable network graphic (*.png)|*.png", Title = "Save render frame"};
@ -69,6 +76,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
{ {
_coreService.FrameRendered += CoreServiceOnFrameRendered; _coreService.FrameRendered += CoreServiceOnFrameRendered;
_coreService.FrameRendering += CoreServiceOnFrameRendering; _coreService.FrameRendering += CoreServiceOnFrameRendering;
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
base.OnActivate(); base.OnActivate();
} }
@ -88,8 +97,6 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
RenderWidth = bitmapInfo.Width; RenderWidth = bitmapInfo.Width;
// ReSharper disable twice CompareOfFloatsByEqualityOperator // ReSharper disable twice CompareOfFloatsByEqualityOperator
if (CurrentFrame is not WriteableBitmap writable || writable.Width != bitmapInfo.Width || writable.Height != bitmapInfo.Height) if (CurrentFrame is not WriteableBitmap writable || writable.Width != bitmapInfo.Width || writable.Height != bitmapInfo.Height)
{ {
CurrentFrame = e.Texture.Surface.Snapshot().ToWriteableBitmap(); CurrentFrame = e.Texture.Surface.Snapshot().ToWriteableBitmap();

View File

@ -42,7 +42,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
IUpdateService updateService, IUpdateService updateService,
IPluginManagementService pluginManagementService, IPluginManagementService pluginManagementService,
IMessageService messageService, IMessageService messageService,
IRegistrationService registrationService) IRegistrationService registrationService,
ICoreService coreService
)
{ {
DisplayName = "GENERAL"; DisplayName = "GENERAL";
@ -64,6 +66,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
TargetFrameRates = new List<Tuple<string, int>>(); TargetFrameRates = new List<Tuple<string, int>>();
for (int i = 10; i <= 30; i += 5) for (int i = 10; i <= 30; i += 5)
TargetFrameRates.Add(new Tuple<string, int>(i + " FPS", i)); TargetFrameRates.Add(new Tuple<string, int>(i + " FPS", i));
if (coreService.StartupArguments.Contains("--pcmr"))
{
TargetFrameRates.Add(new Tuple<string, int>("60 FPS (lol)", 60));
TargetFrameRates.Add(new Tuple<string, int>("144 FPS (omegalol)", 144));
}
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>(); List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();