mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'vulkan' into development
This commit is contained in:
commit
b3b7d6819d
@ -2,7 +2,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Artemis.Core.JsonConverters;
|
using Artemis.Core.JsonConverters;
|
||||||
|
using Artemis.Core.Services;
|
||||||
using Artemis.Core.Services.Core;
|
using Artemis.Core.Services.Core;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
@ -116,5 +118,11 @@ namespace Artemis.Core
|
|||||||
typeof(double),
|
typeof(double),
|
||||||
typeof(decimal)
|
typeof(decimal)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the graphics context to be used for rendering by SkiaSharp. Can be set via
|
||||||
|
/// <see cref="IRgbService.UpdateGraphicsContext" />.
|
||||||
|
/// </summary>
|
||||||
|
public static IManagedGraphicsContext? ManagedGraphicsContext { get; internal set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents SkiaSharp graphics-context related errors
|
||||||
|
/// </summary>
|
||||||
|
public class ArtemisGraphicsContextException : Exception
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ArtemisGraphicsContextException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ArtemisGraphicsContextException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ArtemisGraphicsContextException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/Artemis.Core/Extensions/SKPaintExtensions.cs
Normal file
16
src/Artemis.Core/Extensions/SKPaintExtensions.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal static class SKPaintExtensions
|
||||||
|
{
|
||||||
|
internal static void DisposeSelfAndProperties(this SKPaint paint)
|
||||||
|
{
|
||||||
|
paint.ImageFilter?.Dispose();
|
||||||
|
paint.ColorFilter?.Dispose();
|
||||||
|
paint.MaskFilter?.Dispose();
|
||||||
|
paint.Shader?.Dispose();
|
||||||
|
paint.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -168,7 +168,7 @@ namespace Artemis.Core
|
|||||||
#region Rendering
|
#region Rendering
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Render(SKCanvas canvas)
|
public override void Render(SKCanvas canvas, SKPoint basePosition)
|
||||||
{
|
{
|
||||||
if (Disposed)
|
if (Disposed)
|
||||||
throw new ObjectDisposedException("Folder");
|
throw new ObjectDisposedException("Folder");
|
||||||
@ -189,41 +189,38 @@ namespace Artemis.Core
|
|||||||
baseLayerEffect.Update(Timeline.Delta.TotalSeconds);
|
baseLayerEffect.Update(Timeline.Delta.TotalSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SKPaint layerPaint = new();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
canvas.Save();
|
SKRect rendererBounds = SKRect.Create(0, 0, Path.Bounds.Width, Path.Bounds.Height);
|
||||||
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))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PreProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
baseLayerEffect.PreProcess(canvas, rendererBounds, layerPaint);
|
||||||
|
|
||||||
|
canvas.SaveLayer(layerPaint);
|
||||||
|
canvas.Translate(Path.Bounds.Left - basePosition.X, Path.Bounds.Top - basePosition.Y);
|
||||||
|
|
||||||
// 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));
|
layerPaint.Color = layerPaint.Color.WithAlpha((byte) (layerPaint.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
|
||||||
if (Renderer.Paint.Color.Alpha == 0)
|
if (layerPaint.Color.Alpha == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Iterate the children in reverse because the first layer must be rendered last to end up on top
|
// 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);
|
Children[index].Render(canvas, new SKPoint(Path.Bounds.Left, Path.Bounds.Top));
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PostProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
baseLayerEffect.PostProcess(canvas, rendererBounds, layerPaint);
|
||||||
|
|
||||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
canvas.Restore();
|
canvas.Restore();
|
||||||
Renderer.Close();
|
layerPaint.DisposeSelfAndProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timeline.ClearDelta();
|
Timeline.ClearDelta();
|
||||||
@ -239,7 +236,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
foreach (ProfileElement profileElement in Children)
|
foreach (ProfileElement profileElement in Children)
|
||||||
profileElement.Dispose();
|
profileElement.Dispose();
|
||||||
Renderer.Dispose();
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,6 @@ namespace Artemis.Core
|
|||||||
_layerBrush?.Dispose();
|
_layerBrush?.Dispose();
|
||||||
_general.Dispose();
|
_general.Dispose();
|
||||||
_transform.Dispose();
|
_transform.Dispose();
|
||||||
Renderer.Dispose();
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
@ -183,7 +182,7 @@ namespace Artemis.Core
|
|||||||
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
|
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
|
||||||
ApplyShapeType();
|
ApplyShapeType();
|
||||||
ActivateLayerBrush();
|
ActivateLayerBrush();
|
||||||
|
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +277,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Render(SKCanvas canvas)
|
public override void Render(SKCanvas canvas, SKPoint basePosition)
|
||||||
{
|
{
|
||||||
if (Disposed)
|
if (Disposed)
|
||||||
throw new ObjectDisposedException("Layer");
|
throw new ObjectDisposedException("Layer");
|
||||||
@ -290,9 +289,9 @@ namespace Artemis.Core
|
|||||||
if (LayerBrush == null || LayerBrush?.BaseProperties?.PropertiesInitialized == false)
|
if (LayerBrush == null || LayerBrush?.BaseProperties?.PropertiesInitialized == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RenderTimeline(Timeline, canvas);
|
RenderTimeline(Timeline, canvas, basePosition);
|
||||||
foreach (Timeline extraTimeline in Timeline.ExtraTimelines.ToList())
|
foreach (Timeline extraTimeline in Timeline.ExtraTimelines.ToList())
|
||||||
RenderTimeline(extraTimeline, canvas);
|
RenderTimeline(extraTimeline, canvas, basePosition);
|
||||||
Timeline.ClearDelta();
|
Timeline.ClearDelta();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,36 +312,40 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderTimeline(Timeline timeline, SKCanvas canvas)
|
private void RenderTimeline(Timeline timeline, SKCanvas canvas, SKPoint basePosition)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
if (LayerBrush?.BrushType != LayerBrushType.Regular)
|
if (LayerBrush?.BrushType != LayerBrushType.Regular)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
SKPaint layerPaint = new();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
canvas.Save();
|
canvas.Save();
|
||||||
Renderer.Open(Path, Parent as Folder);
|
canvas.Translate(Path.Bounds.Left - basePosition.X, Path.Bounds.Top - basePosition.Y);
|
||||||
|
using SKPath clipPath = new(Path);
|
||||||
|
clipPath.Transform(SKMatrix.CreateTranslation(Path.Bounds.Left * -1, Path.Bounds.Top * -1));
|
||||||
|
canvas.ClipPath(clipPath);
|
||||||
|
|
||||||
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
|
SKRect layerBounds = SKRect.Create(0, 0, Path.Bounds.Width, Path.Bounds.Height);
|
||||||
throw new ArtemisCoreException("Failed to open layer render context");
|
|
||||||
|
|
||||||
// Apply blend mode and color
|
// Apply blend mode and color
|
||||||
Renderer.Paint.BlendMode = General.BlendMode.CurrentValue;
|
layerPaint.BlendMode = General.BlendMode.CurrentValue;
|
||||||
Renderer.Paint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
|
layerPaint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
|
||||||
|
|
||||||
using SKPath renderPath = new();
|
using SKPath renderPath = new();
|
||||||
|
|
||||||
if (General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
|
if (General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
|
||||||
renderPath.AddRect(Renderer.Path.Bounds);
|
renderPath.AddRect(layerBounds);
|
||||||
else
|
else
|
||||||
renderPath.AddOval(Renderer.Path.Bounds);
|
renderPath.AddOval(layerBounds);
|
||||||
|
|
||||||
if (General.TransformMode.CurrentValue == LayerTransformMode.Normal)
|
if (General.TransformMode.CurrentValue == LayerTransformMode.Normal)
|
||||||
{
|
{
|
||||||
@ -357,54 +360,50 @@ namespace Artemis.Core
|
|||||||
if (LayerBrush.SupportsTransformation)
|
if (LayerBrush.SupportsTransformation)
|
||||||
{
|
{
|
||||||
SKMatrix rotationMatrix = GetTransformMatrix(true, false, false, true);
|
SKMatrix rotationMatrix = GetTransformMatrix(true, false, false, true);
|
||||||
Renderer.Canvas.SetMatrix(Renderer.Canvas.TotalMatrix.PreConcat(rotationMatrix));
|
canvas.SetMatrix(canvas.TotalMatrix.PreConcat(rotationMatrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
DelegateRendering(canvas, renderPath, renderPath.Bounds, layerPaint);
|
||||||
Renderer.Canvas.ClipPath(renderPath);
|
|
||||||
DelegateRendering(renderPath.Bounds);
|
|
||||||
}
|
}
|
||||||
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
|
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
|
||||||
{
|
{
|
||||||
SKMatrix renderPathMatrix = GetTransformMatrix(true, true, true, true);
|
SKMatrix renderPathMatrix = GetTransformMatrix(true, true, true, true);
|
||||||
renderPath.Transform(renderPathMatrix);
|
renderPath.Transform(renderPathMatrix);
|
||||||
|
|
||||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
DelegateRendering(canvas, renderPath, layerBounds, layerPaint);
|
||||||
Renderer.Canvas.ClipPath(renderPath);
|
|
||||||
DelegateRendering(Renderer.Path.Bounds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
try
|
canvas.Restore();
|
||||||
{
|
layerPaint.DisposeSelfAndProperties();
|
||||||
canvas.Restore();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
Renderer.Close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DelegateRendering(SKRect bounds)
|
private void DelegateRendering(SKCanvas canvas, SKPath renderPath, SKRect bounds, SKPaint layerPaint)
|
||||||
{
|
{
|
||||||
if (LayerBrush == null)
|
if (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 (Renderer.Canvas == null || Renderer.Paint == null)
|
|
||||||
throw new ArtemisCoreException("Failed to open layer render context");
|
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PreProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
baseLayerEffect.PreProcess(canvas, bounds, layerPaint);
|
||||||
|
|
||||||
LayerBrush.InternalRender(Renderer.Canvas, bounds, Renderer.Paint);
|
try
|
||||||
|
{
|
||||||
|
canvas.SaveLayer(layerPaint);
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
||||||
baseLayerEffect.PostProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
canvas.ClipPath(renderPath);
|
||||||
|
LayerBrush.InternalRender(canvas, bounds, layerPaint);
|
||||||
|
|
||||||
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
|
baseLayerEffect.PostProcess(canvas, bounds, layerPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
canvas.Restore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CalculateRenderProperties()
|
internal void CalculateRenderProperties()
|
||||||
|
|||||||
@ -81,7 +81,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Render(SKCanvas canvas)
|
public override void Render(SKCanvas canvas, SKPoint basePosition)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@ -91,7 +91,7 @@ namespace Artemis.Core
|
|||||||
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
|
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
|
||||||
|
|
||||||
foreach (ProfileElement profileElement in Children)
|
foreach (ProfileElement profileElement in Children)
|
||||||
profileElement.Render(canvas);
|
profileElement.Render(canvas, basePosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -104,7 +104,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renders the element
|
/// Renders the element
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void Render(SKCanvas canvas);
|
public abstract void Render(SKCanvas canvas, SKPoint basePosition);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the internal state of the element
|
/// Resets the internal state of the element
|
||||||
|
|||||||
@ -19,7 +19,6 @@ namespace Artemis.Core
|
|||||||
internal RenderProfileElement(Profile profile) : base(profile)
|
internal RenderProfileElement(Profile profile) : base(profile)
|
||||||
{
|
{
|
||||||
Timeline = new Timeline();
|
Timeline = new Timeline();
|
||||||
Renderer = new Renderer();
|
|
||||||
ExpandedPropertyGroups = new List<string>();
|
ExpandedPropertyGroups = new List<string>();
|
||||||
LayerEffectsList = new List<BaseLayerEffect>();
|
LayerEffectsList = new List<BaseLayerEffect>();
|
||||||
|
|
||||||
@ -127,7 +126,6 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
base.Parent = value;
|
base.Parent = value;
|
||||||
OnPropertyChanged(nameof(Parent));
|
OnPropertyChanged(nameof(Parent));
|
||||||
Renderer.Invalidate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +142,6 @@ 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +154,6 @@ 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
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,8 @@ namespace Artemis.Core
|
|||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private SKRect _lastBounds;
|
private SKRect _lastBounds;
|
||||||
private SKRect _lastParentBounds;
|
private SKRect _lastParentBounds;
|
||||||
public SKBitmap? Bitmap { get; private set; }
|
private GRContext? _lastGraphicsContext;
|
||||||
public SKCanvas? Canvas { get; private set; }
|
public SKSurface? Surface { 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 SKPoint TargetLocation { get; private set; }
|
public SKPoint TargetLocation { get; private set; }
|
||||||
@ -28,35 +28,40 @@ 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");
|
||||||
|
|
||||||
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds))
|
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds) || _lastGraphicsContext != Constants.ManagedGraphicsContext?.GraphicsContext)
|
||||||
Invalidate();
|
Invalidate();
|
||||||
|
|
||||||
if (!_valid || Canvas == null)
|
if (!_valid || Surface == null)
|
||||||
{
|
{
|
||||||
SKRect pathBounds = path.Bounds;
|
SKRect pathBounds = path.Bounds;
|
||||||
int width = (int) pathBounds.Width;
|
int width = (int) pathBounds.Width;
|
||||||
int height = (int) pathBounds.Height;
|
int height = (int) pathBounds.Height;
|
||||||
|
|
||||||
Bitmap = new SKBitmap(width, height);
|
SKImageInfo imageInfo = new(width, height);
|
||||||
|
if (Constants.ManagedGraphicsContext?.GraphicsContext == null)
|
||||||
|
Surface = SKSurface.Create(imageInfo);
|
||||||
|
else
|
||||||
|
Surface = SKSurface.Create(Constants.ManagedGraphicsContext.GraphicsContext, true, imageInfo);
|
||||||
|
|
||||||
Path = new SKPath(path);
|
Path = new SKPath(path);
|
||||||
Canvas = new SKCanvas(Bitmap);
|
|
||||||
Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
|
Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
|
||||||
|
|
||||||
TargetLocation = new SKPoint(pathBounds.Location.X, pathBounds.Location.Y);
|
TargetLocation = new SKPoint(pathBounds.Location.X, pathBounds.Location.Y);
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
TargetLocation -= parent.Bounds.Location;
|
TargetLocation -= parent.Bounds.Location;
|
||||||
|
|
||||||
Canvas.ClipPath(Path);
|
Surface.Canvas.ClipPath(Path);
|
||||||
|
|
||||||
_lastParentBounds = parent?.Bounds ?? new SKRect();
|
_lastParentBounds = parent?.Bounds ?? new SKRect();
|
||||||
_lastBounds = path.Bounds;
|
_lastBounds = path.Bounds;
|
||||||
|
_lastGraphicsContext = Constants.ManagedGraphicsContext?.GraphicsContext;
|
||||||
_valid = true;
|
_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint = new SKPaint();
|
Paint = new SKPaint();
|
||||||
|
|
||||||
Canvas.Clear();
|
Surface.Canvas.Clear();
|
||||||
Canvas.Save();
|
Surface.Canvas.Save();
|
||||||
|
|
||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
}
|
}
|
||||||
@ -66,8 +71,15 @@ namespace Artemis.Core
|
|||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("Renderer");
|
throw new ObjectDisposedException("Renderer");
|
||||||
|
|
||||||
Canvas?.Restore();
|
Surface?.Canvas.Restore();
|
||||||
|
|
||||||
|
// Looks like every part of the paint needs to be disposed :(
|
||||||
|
Paint?.ColorFilter?.Dispose();
|
||||||
|
Paint?.ImageFilter?.Dispose();
|
||||||
|
Paint?.MaskFilter?.Dispose();
|
||||||
|
Paint?.PathEffect?.Dispose();
|
||||||
Paint?.Dispose();
|
Paint?.Dispose();
|
||||||
|
|
||||||
Paint = null;
|
Paint = null;
|
||||||
|
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
@ -86,17 +98,21 @@ namespace Artemis.Core
|
|||||||
if (IsOpen)
|
if (IsOpen)
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
Canvas?.Dispose();
|
Surface?.Dispose();
|
||||||
Paint?.Dispose();
|
Paint?.Dispose();
|
||||||
Path?.Dispose();
|
Path?.Dispose();
|
||||||
Bitmap?.Dispose();
|
|
||||||
|
|
||||||
Canvas = null;
|
Surface = null;
|
||||||
Paint = null;
|
Paint = null;
|
||||||
Path = null;
|
Path = null;
|
||||||
Bitmap = null;
|
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Renderer()
|
||||||
|
{
|
||||||
|
if (IsOpen)
|
||||||
|
Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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,19 +166,22 @@ 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)
|
||||||
{
|
{
|
||||||
// Render the profile
|
// Render the profile
|
||||||
ActiveProfile?.Render(canvas);
|
ActiveProfile?.Render(canvas, SKPoint.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using RGB.NET.Presets.Textures.Sampler;
|
using RGB.NET.Presets.Textures.Sampler;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@ -10,25 +12,45 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
||||||
{
|
{
|
||||||
private bool _disposed;
|
private readonly SKPixmap _pixelData;
|
||||||
|
private readonly IntPtr _pixelDataPtr;
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal SKTexture(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())
|
|
||||||
{
|
{
|
||||||
Bitmap = new SKBitmap(new SKImageInfo(width, height, SKColorType.Rgb888x));
|
ImageInfo = new SKImageInfo(width, height);
|
||||||
RenderScale = renderScale;
|
Surface = graphicsContext == null
|
||||||
|
? SKSurface.Create(ImageInfo)
|
||||||
|
: SKSurface.Create(graphicsContext.GraphicsContext, true, ImageInfo);
|
||||||
|
RenderScale = scale;
|
||||||
|
|
||||||
|
_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()
|
||||||
|
{
|
||||||
|
using SKImage skImage = Surface.Snapshot();
|
||||||
|
skImage.ReadPixels(_pixelData);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override Color GetColor(in ReadOnlySpan<byte> pixel)
|
protected override Color GetColor(in ReadOnlySpan<byte> pixel)
|
||||||
{
|
{
|
||||||
return new(pixel[0], pixel[1], pixel[2]);
|
return new(pixel[2], pixel[1], pixel[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -38,12 +60,17 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the SKBitmap backing this texture
|
/// Gets the SKBitmap backing this texture
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SKBitmap Bitmap { get; }
|
public SKSurface Surface { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the image info used to create the <see cref="Surface" />
|
||||||
|
/// </summary>
|
||||||
|
public SKImageInfo ImageInfo { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the color data in RGB format
|
/// Gets the color data in RGB format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override ReadOnlySpan<byte> Data => _disposed ? new ReadOnlySpan<byte>() : Bitmap.GetPixelSpan();
|
protected override ReadOnlySpan<byte> Data => _pixelData.GetPixelSpan();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the render scale of the texture
|
/// Gets the render scale of the texture
|
||||||
@ -56,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()
|
||||||
{
|
{
|
||||||
_disposed = true;
|
Surface.Dispose();
|
||||||
Bitmap.Dispose();
|
_pixelData.Dispose();
|
||||||
|
|
||||||
|
ReleaseUnmanagedResources();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
~SKTexture()
|
||||||
|
{
|
||||||
|
ReleaseUnmanagedResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -59,6 +59,7 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
UpdatePluginCache();
|
UpdatePluginCache();
|
||||||
|
|
||||||
|
_rgbService.IsRenderPaused = true;
|
||||||
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
_rgbService.Surface.Updating += SurfaceOnUpdating;
|
||||||
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
|
||||||
|
|
||||||
@ -139,21 +140,23 @@ namespace Artemis.Core.Services
|
|||||||
module.InternalUpdate(args.DeltaTime);
|
module.InternalUpdate(args.DeltaTime);
|
||||||
|
|
||||||
// Render all active modules
|
// Render all active modules
|
||||||
SKTexture texture =_rgbService.OpenRender();
|
SKTexture texture = _rgbService.OpenRender();
|
||||||
|
|
||||||
using (SKCanvas canvas = new(texture.Bitmap))
|
SKCanvas canvas = texture.Surface.Canvas;
|
||||||
|
canvas.Save();
|
||||||
|
canvas.Scale(texture.RenderScale);
|
||||||
|
canvas.Clear(new SKColor(0, 0, 0));
|
||||||
|
|
||||||
|
// While non-activated modules may be updated above if they expand the main data model, they may never render
|
||||||
|
if (!ModuleRenderingDisabled)
|
||||||
{
|
{
|
||||||
canvas.Scale(texture.RenderScale);
|
foreach (Module module in modules.Where(m => m.IsActivated))
|
||||||
canvas.Clear(new SKColor(0, 0, 0));
|
module.InternalRender(args.DeltaTime, canvas, texture.ImageInfo);
|
||||||
// While non-activated modules may be updated above if they expand the main data model, they may never render
|
|
||||||
if (!ModuleRenderingDisabled)
|
|
||||||
{
|
|
||||||
foreach (Module module in modules.Where(m => m.IsActivated))
|
|
||||||
module.InternalRender(args.DeltaTime, canvas, texture.Bitmap.Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
||||||
|
canvas.RestoreToCount(-1);
|
||||||
|
canvas.Flush();
|
||||||
|
|
||||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
||||||
}
|
}
|
||||||
@ -163,9 +166,9 @@ namespace Artemis.Core.Services
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
_rgbService.CloseRender();
|
||||||
_frameStopWatch.Stop();
|
_frameStopWatch.Stop();
|
||||||
FrameTime = _frameStopWatch.Elapsed;
|
FrameTime = _frameStopWatch.Elapsed;
|
||||||
_rgbService.CloseRender();
|
|
||||||
|
|
||||||
LogUpdateExceptions();
|
LogUpdateExceptions();
|
||||||
}
|
}
|
||||||
@ -240,6 +243,7 @@ namespace Artemis.Core.Services
|
|||||||
IsElevated
|
IsElevated
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_rgbService.IsRenderPaused = false;
|
||||||
OnInitialized();
|
OnInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
@ -50,6 +51,16 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void CloseRender();
|
void CloseRender();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the graphics context to the provided <paramref name="managedGraphicsContext"></paramref>.
|
||||||
|
/// <para>Note: The old graphics context will be used until the next frame starts rendering and is disposed afterwards.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="managedGraphicsContext">
|
||||||
|
/// The new managed graphics context. If <see langword="null" />, software rendering
|
||||||
|
/// is used.
|
||||||
|
/// </param>
|
||||||
|
void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the given device provider to the <see cref="Surface" />
|
/// Adds the given device provider to the <see cref="Surface" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -4,11 +4,11 @@ using System.Collections.ObjectModel;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.Services.Models;
|
using Artemis.Core.Services.Models;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
using Artemis.Storage.Entities.Surface;
|
using Artemis.Storage.Entities.Surface;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
@ -51,6 +51,8 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
|
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
|
||||||
Surface.RegisterUpdateTrigger(UpdateTrigger);
|
Surface.RegisterUpdateTrigger(UpdateTrigger);
|
||||||
|
|
||||||
|
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimerUpdateTrigger UpdateTrigger { get; }
|
public TimerUpdateTrigger UpdateTrigger { get; }
|
||||||
@ -66,6 +68,11 @@ namespace Artemis.Core.Services
|
|||||||
_texture?.Invalidate();
|
_texture?.Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
IsRenderPaused = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void SurfaceOnLayoutChanged(SurfaceLayoutChangedEventArgs args)
|
private void SurfaceOnLayoutChanged(SurfaceLayoutChangedEventArgs args)
|
||||||
{
|
{
|
||||||
UpdateLedGroup();
|
UpdateLedGroup();
|
||||||
@ -115,13 +122,16 @@ namespace Artemis.Core.Services
|
|||||||
DeviceAdded?.Invoke(this, e);
|
DeviceAdded?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_texture?.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
public IReadOnlyCollection<ArtemisDevice> EnabledDevices => _enabledDevices.AsReadOnly();
|
public IReadOnlyCollection<ArtemisDevice> EnabledDevices => _enabledDevices.AsReadOnly();
|
||||||
public IReadOnlyCollection<ArtemisDevice> Devices => _devices.AsReadOnly();
|
public IReadOnlyCollection<ArtemisDevice> Devices => _devices.AsReadOnly();
|
||||||
public IReadOnlyDictionary<Led, ArtemisLed> LedMap => new ReadOnlyDictionary<Led, ArtemisLed>(_ledMap);
|
public IReadOnlyDictionary<Led, ArtemisLed> LedMap => new ReadOnlyDictionary<Led, ArtemisLed>(_ledMap);
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public RGBSurface Surface { get; set; }
|
public RGBSurface Surface { get; set; }
|
||||||
|
|
||||||
public bool IsRenderPaused { get; set; }
|
public bool IsRenderPaused { get; set; }
|
||||||
public bool RenderOpen { get; private set; }
|
public bool RenderOpen { get; private set; }
|
||||||
|
|
||||||
@ -197,11 +207,6 @@ namespace Artemis.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
_texture?.Invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Surface.UnregisterUpdateTrigger(UpdateTrigger);
|
Surface.UnregisterUpdateTrigger(UpdateTrigger);
|
||||||
@ -216,6 +221,8 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
#region Rendering
|
#region Rendering
|
||||||
|
|
||||||
|
private IManagedGraphicsContext? _newGraphicsContext;
|
||||||
|
|
||||||
public SKTexture OpenRender()
|
public SKTexture OpenRender()
|
||||||
{
|
{
|
||||||
if (RenderOpen)
|
if (RenderOpen)
|
||||||
@ -234,6 +241,7 @@ namespace Artemis.Core.Services
|
|||||||
throw new ArtemisCoreException("Render pipeline is already closed");
|
throw new ArtemisCoreException("Render pipeline is already closed");
|
||||||
|
|
||||||
RenderOpen = false;
|
RenderOpen = false;
|
||||||
|
_texture?.CopyPixelData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateTexture()
|
public void CreateTexture()
|
||||||
@ -241,15 +249,38 @@ namespace Artemis.Core.Services
|
|||||||
if (RenderOpen)
|
if (RenderOpen)
|
||||||
throw new ArtemisCoreException("Cannot update the texture while rendering");
|
throw new ArtemisCoreException("Cannot update the texture while rendering");
|
||||||
|
|
||||||
SKTexture? oldTexture = _texture;
|
IManagedGraphicsContext? graphicsContext = Constants.ManagedGraphicsContext = _newGraphicsContext;
|
||||||
|
if (!ReferenceEquals(graphicsContext, _newGraphicsContext))
|
||||||
|
graphicsContext = _newGraphicsContext;
|
||||||
|
|
||||||
|
if (graphicsContext != null)
|
||||||
|
_logger.Debug("Creating SKTexture with graphics context {graphicsContext}", graphicsContext.GetType().Name);
|
||||||
|
else
|
||||||
|
_logger.Debug("Creating SKTexture with software-based graphics context");
|
||||||
|
|
||||||
float renderScale = (float) _renderScaleSetting.Value;
|
float renderScale = (float) _renderScaleSetting.Value;
|
||||||
int width = Math.Max(1, MathF.Min(Surface.Boundary.Size.Width * renderScale, 4096).RoundToInt());
|
int width = Math.Max(1, MathF.Min(Surface.Boundary.Size.Width * renderScale, 4096).RoundToInt());
|
||||||
int height = Math.Max(1, MathF.Min(Surface.Boundary.Size.Height * renderScale, 4096).RoundToInt());
|
int height = Math.Max(1, MathF.Min(Surface.Boundary.Size.Height * renderScale, 4096).RoundToInt());
|
||||||
_texture = new SKTexture(width, height, renderScale);
|
_texture?.Dispose();
|
||||||
|
_texture = new SKTexture(graphicsContext, width, height, renderScale);
|
||||||
_textureBrush.Texture = _texture;
|
_textureBrush.Texture = _texture;
|
||||||
|
|
||||||
oldTexture?.Dispose();
|
|
||||||
|
if (!ReferenceEquals(_newGraphicsContext, Constants.ManagedGraphicsContext = _newGraphicsContext))
|
||||||
|
{
|
||||||
|
Constants.ManagedGraphicsContext?.Dispose();
|
||||||
|
Constants.ManagedGraphicsContext = _newGraphicsContext;
|
||||||
|
_newGraphicsContext = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(managedGraphicsContext, Constants.ManagedGraphicsContext))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_newGraphicsContext = managedGraphicsContext;
|
||||||
|
_texture?.Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
16
src/Artemis.Core/SkiaSharp/IManagedGraphicsContext.cs
Normal file
16
src/Artemis.Core/SkiaSharp/IManagedGraphicsContext.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.SkiaSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a managed wrapper around a SkiaSharp context
|
||||||
|
/// </summary>
|
||||||
|
public interface IManagedGraphicsContext : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the graphics context created by this wrapper
|
||||||
|
/// </summary>
|
||||||
|
GRContext GraphicsContext { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,7 +30,7 @@ namespace Artemis.Core
|
|||||||
public void Render(double deltaTime, SKCanvas canvas)
|
public void Render(double deltaTime, SKCanvas canvas)
|
||||||
{
|
{
|
||||||
AnimationProfile.Update(deltaTime);
|
AnimationProfile.Update(deltaTime);
|
||||||
AnimationProfile.Render(canvas);
|
AnimationProfile.Render(canvas, SKPoint.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Profile CreateIntroProfile()
|
private Profile CreateIntroProfile()
|
||||||
|
|||||||
@ -149,6 +149,7 @@
|
|||||||
<PackageReference Include="RawInput.Sharp" Version="0.0.3" />
|
<PackageReference Include="RawInput.Sharp" Version="0.0.3" />
|
||||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||||
<PackageReference Include="SkiaSharp.Views.WPF" Version="2.80.2" />
|
<PackageReference Include="SkiaSharp.Views.WPF" Version="2.80.2" />
|
||||||
|
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.80.2" />
|
||||||
<PackageReference Include="Stylet" Version="1.3.5" />
|
<PackageReference Include="Stylet" Version="1.3.5" />
|
||||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
|||||||
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Markup;
|
using System.Windows.Markup;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject;
|
using Artemis.UI.Ninject;
|
||||||
@ -13,9 +14,11 @@ using Artemis.UI.Screens;
|
|||||||
using Artemis.UI.Services;
|
using Artemis.UI.Services;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.UI.SkiaSharp;
|
||||||
using Artemis.UI.Stylet;
|
using Artemis.UI.Stylet;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using SkiaSharp;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.UI
|
namespace Artemis.UI
|
||||||
@ -65,7 +68,7 @@ namespace Artemis.UI
|
|||||||
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
|
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
|
||||||
|
|
||||||
// Create and bind the root view, this is a tray icon so don't show it with the window manager
|
// Create and bind the root view, this is a tray icon so don't show it with the window manager
|
||||||
Execute.OnUIThread(() =>
|
Execute.OnUIThreadSync(() =>
|
||||||
{
|
{
|
||||||
UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel);
|
UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel);
|
||||||
((TrayViewModel) RootViewModel).SetTaskbarIcon(view);
|
((TrayViewModel) RootViewModel).SetTaskbarIcon(view);
|
||||||
@ -90,7 +93,12 @@ namespace Artemis.UI
|
|||||||
IRegistrationService registrationService = Kernel.Get<IRegistrationService>();
|
IRegistrationService registrationService = Kernel.Get<IRegistrationService>();
|
||||||
registrationService.RegisterInputProvider();
|
registrationService.RegisterInputProvider();
|
||||||
registrationService.RegisterControllers();
|
registrationService.RegisterControllers();
|
||||||
|
|
||||||
|
Execute.OnUIThreadSync(() =>
|
||||||
|
{
|
||||||
|
registrationService.ApplyPreferredGraphicsContext();
|
||||||
|
});
|
||||||
|
|
||||||
// Initialize background services
|
// Initialize background services
|
||||||
Kernel.Get<IDeviceLayoutService>();
|
Kernel.Get<IDeviceLayoutService>();
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/Artemis.UI/Exceptions/ArtemisGraphicsContextException.cs
Normal file
22
src/Artemis.UI/Exceptions/ArtemisGraphicsContextException.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Exceptions
|
||||||
|
{
|
||||||
|
public class ArtemisGraphicsContextException : Exception
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ArtemisGraphicsContextException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ArtemisGraphicsContextException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ArtemisGraphicsContextException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"Artemis.UI": {
|
"Artemis.UI": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"commandLineArgs": "--force-elevation"
|
"commandLineArgs": "--force-elevation --pcmr"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,9 +32,10 @@
|
|||||||
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,5,0">
|
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,5,0">
|
||||||
<Run Text="FPS: " />
|
<Run Text="FPS: " />
|
||||||
<Run FontWeight="Bold" Text="{Binding CurrentFps}" />
|
<Run FontWeight="Bold" Text="{Binding CurrentFps}" />
|
||||||
<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>
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,9 @@ 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;
|
||||||
|
private int _frames;
|
||||||
|
private DateTime _frameCountStart;
|
||||||
|
|
||||||
public RenderDebugViewModel(ICoreService coreService)
|
public RenderDebugViewModel(ICoreService coreService)
|
||||||
{
|
{
|
||||||
@ -51,6 +54,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"};
|
||||||
@ -81,38 +90,38 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
|||||||
|
|
||||||
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
|
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
|
||||||
{
|
{
|
||||||
|
using SKImage skImage = e.Texture.Surface.Snapshot();
|
||||||
|
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||||
|
|
||||||
|
if (_frameTargetPath != null)
|
||||||
|
{
|
||||||
|
using (SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100))
|
||||||
|
{
|
||||||
|
using (FileStream stream = File.OpenWrite(_frameTargetPath))
|
||||||
|
{
|
||||||
|
data.SaveTo(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_frameTargetPath = null;
|
||||||
|
}
|
||||||
|
|
||||||
Execute.OnUIThreadSync(() =>
|
Execute.OnUIThreadSync(() =>
|
||||||
{
|
{
|
||||||
SKImageInfo bitmapInfo = e.Texture.Bitmap.Info;
|
|
||||||
RenderHeight = bitmapInfo.Height;
|
RenderHeight = bitmapInfo.Height;
|
||||||
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.Bitmap.ToWriteableBitmap();
|
CurrentFrame = e.Texture.Surface.Snapshot().ToWriteableBitmap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using SKImage skImage = SKImage.FromPixels(e.Texture.Bitmap.PeekPixels());
|
|
||||||
|
|
||||||
if (_frameTargetPath != null)
|
|
||||||
{
|
|
||||||
using (SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100))
|
|
||||||
{
|
|
||||||
using (FileStream stream = File.OpenWrite(_frameTargetPath))
|
|
||||||
{
|
|
||||||
data.SaveTo(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_frameTargetPath = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
SKImageInfo info = new(skImage.Width, skImage.Height);
|
|
||||||
writable.Lock();
|
writable.Lock();
|
||||||
using (SKPixmap pixmap = new(info, writable.BackBuffer, writable.BackBufferStride))
|
using (SKPixmap pixmap = new(bitmapInfo, writable.BackBuffer, writable.BackBufferStride))
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once AccessToDisposedClosure - Looks fine
|
||||||
skImage.ReadPixels(pixmap, 0, 0);
|
skImage.ReadPixels(pixmap, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +132,16 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
|||||||
|
|
||||||
private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)
|
private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)
|
||||||
{
|
{
|
||||||
CurrentFps = Math.Round(1.0 / e.DeltaTime, 2);
|
if (DateTime.Now - _frameCountStart >= TimeSpan.FromSeconds(1))
|
||||||
|
{
|
||||||
|
CurrentFps = _frames;
|
||||||
|
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||||
|
|
||||||
|
_frames = 0;
|
||||||
|
_frameCountStart = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
_frames++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@
|
|||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
xmlns:dataTemplateSelectors="clr-namespace:Artemis.UI.DataTemplateSelectors"
|
xmlns:dataTemplateSelectors="clr-namespace:Artemis.UI.DataTemplateSelectors"
|
||||||
|
xmlns:system="clr-namespace:System;assembly=System.Runtime"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800"
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance local:GeneralSettingsTabViewModel}">
|
d:DataContext="{d:DesignInstance local:GeneralSettingsTabViewModel}">
|
||||||
@ -307,6 +308,30 @@
|
|||||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Rendering</TextBlock>
|
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Rendering</TextBlock>
|
||||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
<StackPanel Margin="15">
|
<StackPanel Margin="15">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Preferred render method</TextBlock>
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||||
|
Software-based rendering is done purely on the CPU while Vulkan uses GPU-acceleration
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<ComboBox Width="80" SelectedItem="{Binding PreferredGraphicsContext}" >
|
||||||
|
<system:String>Software</system:String>
|
||||||
|
<system:String>Vulkan</system:String>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
|
|||||||
@ -3,15 +3,10 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection.Metadata;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.Core.Services.Core;
|
|
||||||
using Artemis.UI.Properties;
|
|
||||||
using Artemis.UI.Screens.StartupWizard;
|
using Artemis.UI.Screens.StartupWizard;
|
||||||
using Artemis.UI.Services;
|
using Artemis.UI.Services;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
@ -30,6 +25,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IKernel _kernel;
|
private readonly IKernel _kernel;
|
||||||
private readonly IMessageService _messageService;
|
private readonly IMessageService _messageService;
|
||||||
|
private readonly IRegistrationService _registrationService;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IUpdateService _updateService;
|
private readonly IUpdateService _updateService;
|
||||||
private readonly IWindowManager _windowManager;
|
private readonly IWindowManager _windowManager;
|
||||||
@ -45,7 +41,10 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
ISettingsService settingsService,
|
ISettingsService settingsService,
|
||||||
IUpdateService updateService,
|
IUpdateService updateService,
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IMessageService messageService)
|
IMessageService messageService,
|
||||||
|
IRegistrationService registrationService,
|
||||||
|
ICoreService coreService
|
||||||
|
)
|
||||||
{
|
{
|
||||||
DisplayName = "GENERAL";
|
DisplayName = "GENERAL";
|
||||||
|
|
||||||
@ -56,6 +55,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_updateService = updateService;
|
_updateService = updateService;
|
||||||
_messageService = messageService;
|
_messageService = messageService;
|
||||||
|
_registrationService = registrationService;
|
||||||
|
|
||||||
LogLevels = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel)));
|
LogLevels = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel)));
|
||||||
ColorSchemes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(ApplicationColorScheme)));
|
ColorSchemes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(ApplicationColorScheme)));
|
||||||
@ -66,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>();
|
||||||
|
|
||||||
@ -209,6 +214,17 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string PreferredGraphicsContext
|
||||||
|
{
|
||||||
|
get => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Value = value;
|
||||||
|
_settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Save();
|
||||||
|
_registrationService.ApplyPreferredGraphicsContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public double RenderScale
|
public double RenderScale
|
||||||
{
|
{
|
||||||
get => _settingsService.GetSetting("Core.RenderScale", 0.5).Value;
|
get => _settingsService.GetSetting("Core.RenderScale", 0.5).Value;
|
||||||
@ -316,10 +332,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool taskCreated = false;
|
bool taskCreated = false;
|
||||||
if (!recreate)
|
if (!recreate) taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
|
||||||
{
|
|
||||||
taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StartWithWindows && !taskCreated)
|
if (StartWithWindows && !taskCreated)
|
||||||
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(AutoRunDelay));
|
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(AutoRunDelay));
|
||||||
@ -335,7 +348,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public enum ApplicationColorScheme
|
public enum ApplicationColorScheme
|
||||||
{
|
{
|
||||||
Light,
|
Light,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Controllers;
|
using Artemis.UI.Controllers;
|
||||||
@ -8,6 +9,7 @@ using Artemis.UI.DefaultTypes.PropertyInput;
|
|||||||
using Artemis.UI.InputProviders;
|
using Artemis.UI.InputProviders;
|
||||||
using Artemis.UI.Ninject;
|
using Artemis.UI.Ninject;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.UI.SkiaSharp;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Artemis.UI.Services
|
namespace Artemis.UI.Services
|
||||||
@ -15,28 +17,38 @@ namespace Artemis.UI.Services
|
|||||||
public class RegistrationService : IRegistrationService
|
public class RegistrationService : IRegistrationService
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly ICoreService _coreService;
|
||||||
private readonly IDataModelUIService _dataModelUIService;
|
private readonly IDataModelUIService _dataModelUIService;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
private readonly IInputService _inputService;
|
private readonly IInputService _inputService;
|
||||||
private readonly IWebServerService _webServerService;
|
private readonly IWebServerService _webServerService;
|
||||||
|
private readonly IRgbService _rgbService;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
private bool _registeredBuiltInDataModelDisplays;
|
private bool _registeredBuiltInDataModelDisplays;
|
||||||
private bool _registeredBuiltInDataModelInputs;
|
private bool _registeredBuiltInDataModelInputs;
|
||||||
private bool _registeredBuiltInPropertyEditors;
|
private bool _registeredBuiltInPropertyEditors;
|
||||||
|
private VulkanContext _vulkanContext;
|
||||||
|
|
||||||
public RegistrationService(ILogger logger,
|
public RegistrationService(ILogger logger,
|
||||||
|
ICoreService coreService,
|
||||||
IDataModelUIService dataModelUIService,
|
IDataModelUIService dataModelUIService,
|
||||||
IProfileEditorService profileEditorService,
|
IProfileEditorService profileEditorService,
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IInputService inputService,
|
IInputService inputService,
|
||||||
IWebServerService webServerService)
|
IWebServerService webServerService,
|
||||||
|
IRgbService rgbService,
|
||||||
|
ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_coreService = coreService;
|
||||||
_dataModelUIService = dataModelUIService;
|
_dataModelUIService = dataModelUIService;
|
||||||
_profileEditorService = profileEditorService;
|
_profileEditorService = profileEditorService;
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
_inputService = inputService;
|
_inputService = inputService;
|
||||||
_webServerService = webServerService;
|
_webServerService = webServerService;
|
||||||
|
_rgbService = rgbService;
|
||||||
|
_settingsService = settingsService;
|
||||||
|
|
||||||
LoadPluginModules();
|
LoadPluginModules();
|
||||||
pluginManagementService.PluginEnabling += PluginServiceOnPluginEnabling;
|
pluginManagementService.PluginEnabling += PluginServiceOnPluginEnabling;
|
||||||
@ -97,6 +109,40 @@ namespace Artemis.UI.Services
|
|||||||
_webServerService.AddController<RemoteController>();
|
_webServerService.AddController<RemoteController>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ApplyPreferredGraphicsContext()
|
||||||
|
{
|
||||||
|
if (_coreService.StartupArguments.Contains("--force-software-render"))
|
||||||
|
{
|
||||||
|
_logger.Warning("Startup argument '--force-software-render' is applied, forcing software rendering.");
|
||||||
|
_rgbService.UpdateGraphicsContext(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginSetting<string> preferredGraphicsContext = _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (preferredGraphicsContext.Value)
|
||||||
|
{
|
||||||
|
case "Software":
|
||||||
|
_rgbService.UpdateGraphicsContext(null);
|
||||||
|
break;
|
||||||
|
case "Vulkan":
|
||||||
|
_vulkanContext ??= new VulkanContext();
|
||||||
|
_rgbService.UpdateGraphicsContext(_vulkanContext);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Warning(e, "Failed to apply preferred graphics context {preferred}", preferredGraphicsContext.Value);
|
||||||
|
_rgbService.UpdateGraphicsContext(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void PluginServiceOnPluginEnabling(object sender, PluginEventArgs e)
|
private void PluginServiceOnPluginEnabling(object sender, PluginEventArgs e)
|
||||||
{
|
{
|
||||||
e.Plugin.Kernel.Load(new[] {new PluginUIModule(e.Plugin)});
|
e.Plugin.Kernel.Load(new[] {new PluginUIModule(e.Plugin)});
|
||||||
@ -116,5 +162,6 @@ namespace Artemis.UI.Services
|
|||||||
void RegisterBuiltInPropertyEditors();
|
void RegisterBuiltInPropertyEditors();
|
||||||
void RegisterInputProvider();
|
void RegisterInputProvider();
|
||||||
void RegisterControllers();
|
void RegisterControllers();
|
||||||
|
void ApplyPreferredGraphicsContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
src/Artemis.UI/SkiaSharp/Vulkan/Kernel32.cs
Normal file
24
src/Artemis.UI/SkiaSharp/Vulkan/Kernel32.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp.Vulkan
|
||||||
|
{
|
||||||
|
internal class Kernel32
|
||||||
|
{
|
||||||
|
private const string kernel32 = "kernel32.dll";
|
||||||
|
|
||||||
|
static Kernel32()
|
||||||
|
{
|
||||||
|
CurrentModuleHandle = Kernel32.GetModuleHandle(null);
|
||||||
|
if (CurrentModuleHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
throw new Exception("Could not get module handle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr CurrentModuleHandle { get; }
|
||||||
|
|
||||||
|
[DllImport(kernel32, CallingConvention = CallingConvention.Winapi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||||
|
public static extern IntPtr GetModuleHandle([MarshalAs(UnmanagedType.LPTStr)] string lpModuleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/Artemis.UI/SkiaSharp/Vulkan/VkContext.cs
Normal file
35
src/Artemis.UI/SkiaSharp/Vulkan/VkContext.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using SharpVk.Khronos;
|
||||||
|
using SkiaSharp;
|
||||||
|
using Device = SharpVk.Device;
|
||||||
|
using Instance = SharpVk.Instance;
|
||||||
|
using PhysicalDevice = SharpVk.PhysicalDevice;
|
||||||
|
using Queue = SharpVk.Queue;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp.Vulkan
|
||||||
|
{
|
||||||
|
internal class VkContext : IDisposable
|
||||||
|
{
|
||||||
|
public virtual Instance Instance { get; protected set; }
|
||||||
|
|
||||||
|
public virtual PhysicalDevice PhysicalDevice { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Surface Surface { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Device Device { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Queue GraphicsQueue { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Queue PresentQueue { get; protected set; }
|
||||||
|
|
||||||
|
public virtual uint GraphicsFamily { get; protected set; }
|
||||||
|
|
||||||
|
public virtual uint PresentFamily { get; protected set; }
|
||||||
|
|
||||||
|
public virtual GRVkGetProcedureAddressDelegate GetProc { get; protected set; }
|
||||||
|
|
||||||
|
public virtual GRSharpVkGetProcedureAddressDelegate SharpVkGetProc { get; protected set; }
|
||||||
|
|
||||||
|
public virtual void Dispose() => Instance?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/Artemis.UI/SkiaSharp/Vulkan/Win32VkContext.cs
Normal file
86
src/Artemis.UI/SkiaSharp/Vulkan/Win32VkContext.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using SharpVk;
|
||||||
|
using SharpVk.Khronos;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp.Vulkan
|
||||||
|
{
|
||||||
|
internal sealed class Win32VkContext : VkContext
|
||||||
|
{
|
||||||
|
public NativeWindow Window { get; }
|
||||||
|
|
||||||
|
public Win32VkContext()
|
||||||
|
{
|
||||||
|
Window = new NativeWindow();
|
||||||
|
Instance = Instance.Create(null, new[] {"VK_KHR_surface", "VK_KHR_win32_surface"});
|
||||||
|
PhysicalDevice = Instance.EnumeratePhysicalDevices().First();
|
||||||
|
Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, Window.Handle);
|
||||||
|
|
||||||
|
(GraphicsFamily, PresentFamily) = FindQueueFamilies();
|
||||||
|
|
||||||
|
DeviceQueueCreateInfo[] queueInfos =
|
||||||
|
{
|
||||||
|
new() {QueueFamilyIndex = GraphicsFamily, QueuePriorities = new[] {1f}},
|
||||||
|
new() {QueueFamilyIndex = PresentFamily, QueuePriorities = new[] {1f}}
|
||||||
|
};
|
||||||
|
Device = PhysicalDevice.CreateDevice(queueInfos, null, null);
|
||||||
|
GraphicsQueue = Device.GetQueue(GraphicsFamily, 0);
|
||||||
|
PresentQueue = Device.GetQueue(PresentFamily, 0);
|
||||||
|
|
||||||
|
GetProc = (name, instanceHandle, deviceHandle) =>
|
||||||
|
{
|
||||||
|
if (deviceHandle != IntPtr.Zero)
|
||||||
|
return Device.GetProcedureAddress(name);
|
||||||
|
|
||||||
|
return Instance.GetProcedureAddress(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
SharpVkGetProc = (name, instance, device) =>
|
||||||
|
{
|
||||||
|
if (device != null)
|
||||||
|
return device.GetProcedureAddress(name);
|
||||||
|
if (instance != null)
|
||||||
|
return instance.GetProcedureAddress(name);
|
||||||
|
|
||||||
|
// SharpVk includes the static functions on Instance, but this is not actually correct
|
||||||
|
// since the functions are static, they are not tied to an instance. For example,
|
||||||
|
// VkCreateInstance is not found on an instance, it is creating said instance.
|
||||||
|
// Other libraries, such as VulkanCore, use another type to do this.
|
||||||
|
return Instance.GetProcedureAddress(name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
base.Dispose();
|
||||||
|
Window.DestroyHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private (uint, uint) FindQueueFamilies()
|
||||||
|
{
|
||||||
|
QueueFamilyProperties[] queueFamilyProperties = PhysicalDevice.GetQueueFamilyProperties();
|
||||||
|
|
||||||
|
var graphicsFamily = queueFamilyProperties
|
||||||
|
.Select((properties, index) => new {properties, index})
|
||||||
|
.SkipWhile(pair => !pair.properties.QueueFlags.HasFlag(QueueFlags.Graphics))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (graphicsFamily == null)
|
||||||
|
throw new Exception("Unable to find graphics queue");
|
||||||
|
|
||||||
|
uint? presentFamily = default;
|
||||||
|
|
||||||
|
for (uint i = 0; i < queueFamilyProperties.Length; ++i)
|
||||||
|
{
|
||||||
|
if (PhysicalDevice.GetSurfaceSupport(i, Surface))
|
||||||
|
presentFamily = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!presentFamily.HasValue)
|
||||||
|
throw new Exception("Unable to find present queue");
|
||||||
|
|
||||||
|
return ((uint) graphicsFamily.index, presentFamily.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/Artemis.UI/SkiaSharp/VulkanContext.cs
Normal file
64
src/Artemis.UI/SkiaSharp/VulkanContext.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core.SkiaSharp;
|
||||||
|
using Artemis.UI.Exceptions;
|
||||||
|
using Artemis.UI.SkiaSharp.Vulkan;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp
|
||||||
|
{
|
||||||
|
public class VulkanContext : IManagedGraphicsContext
|
||||||
|
{
|
||||||
|
private readonly GRVkBackendContext _vulkanBackendContext;
|
||||||
|
private readonly Win32VkContext _vulkanContext;
|
||||||
|
|
||||||
|
public VulkanContext()
|
||||||
|
{
|
||||||
|
// Try everything in separate try-catch blocks to provide some accuracy in error reporting
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_vulkanContext = new Win32VkContext();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ArtemisGraphicsContextException("Failed to create Vulkan context", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_vulkanBackendContext = new GRVkBackendContext
|
||||||
|
{
|
||||||
|
VkInstance = (IntPtr) _vulkanContext.Instance.RawHandle.ToUInt64(),
|
||||||
|
VkPhysicalDevice = (IntPtr) _vulkanContext.PhysicalDevice.RawHandle.ToUInt64(),
|
||||||
|
VkDevice = (IntPtr) _vulkanContext.Device.RawHandle.ToUInt64(),
|
||||||
|
VkQueue = (IntPtr) _vulkanContext.GraphicsQueue.RawHandle.ToUInt64(),
|
||||||
|
GraphicsQueueIndex = _vulkanContext.GraphicsFamily,
|
||||||
|
GetProcedureAddress = _vulkanContext.GetProc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ArtemisGraphicsContextException("Failed to create Vulkan backend context", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GraphicsContext = GRContext.CreateVulkan(_vulkanBackendContext);
|
||||||
|
if (GraphicsContext == null)
|
||||||
|
throw new ArtemisGraphicsContextException("GRContext.CreateVulkan returned null");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ArtemisGraphicsContextException("Failed to create Vulkan graphics context", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsContext.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public GRContext GraphicsContext { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -126,6 +126,16 @@
|
|||||||
"SkiaSharp.Views.Desktop.Common": "2.80.2"
|
"SkiaSharp.Views.Desktop.Common": "2.80.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SkiaSharp.Vulkan.SharpVk": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[2.80.2, )",
|
||||||
|
"resolved": "2.80.2",
|
||||||
|
"contentHash": "qiqlbgMsSdxTsaPErtE1lXoMXolVVF9E6irmSTzlW++6BbW8tzA89n7GNsgMYJgyo2ljHZhX5ydhFn0Rkj7VHw==",
|
||||||
|
"dependencies": {
|
||||||
|
"SharpVk": "0.4.2",
|
||||||
|
"SkiaSharp": "2.80.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Stylet": {
|
"Stylet": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.3.5, )",
|
"requested": "[1.3.5, )",
|
||||||
@ -513,6 +523,15 @@
|
|||||||
"resolved": "1.7.1",
|
"resolved": "1.7.1",
|
||||||
"contentHash": "ljl9iVpmGOjgmxXxyulMBfl7jCLEMmTOSIrQwJJQLIm5PFhtaxRRgdQPY5ElXz+vfPKqX7Aj3RGnAN+SUN7V3w=="
|
"contentHash": "ljl9iVpmGOjgmxXxyulMBfl7jCLEMmTOSIrQwJJQLIm5PFhtaxRRgdQPY5ElXz+vfPKqX7Aj3RGnAN+SUN7V3w=="
|
||||||
},
|
},
|
||||||
|
"SharpVk": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "0.4.2",
|
||||||
|
"contentHash": "0CzZJWKw6CTmxKOXzCCyTKCD7tZB6g2+tm2VSSCXWTHlIMHxlRzbH5BaqkYCGo9Y23wp0hPuz2U3NifMH1VI6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": "4.4.0",
|
||||||
|
"System.ValueTuple": "4.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"SkiaSharp": {
|
"SkiaSharp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.80.2",
|
"resolved": "2.80.2",
|
||||||
|
|||||||
@ -225,7 +225,9 @@
|
|||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=luma/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=luma/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=pixmap/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=snackbar/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=snackbar/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|
||||||
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Timelines/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Timelines/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vulkan/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
Loading…
x
Reference in New Issue
Block a user