mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Core - Reworked rendering pipeline to work with different contexts
Core - Added GPU-based Vulkan rendering context
This commit is contained in:
1 parent
34b9d69ed8
commit
a02431bb82
17 files changed
+489
-56
No files matched your search
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using Artemis.Core.JsonConverters;
|
||||
using Artemis.Core.Services.Core;
|
||||
using Newtonsoft.Json;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
@@ -116,5 +117,21 @@ namespace Artemis.Core
|
||||
typeof(double),
|
||||
typeof(decimal)
|
||||
};
|
||||
|
||||
private static GRContext? _skiaGraphicsContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the graphics context to be used for rendering by SkiaSharp
|
||||
/// </summary>
|
||||
public static GRContext? SkiaGraphicsContext
|
||||
{
|
||||
get => _skiaGraphicsContext;
|
||||
set
|
||||
{
|
||||
if (_skiaGraphicsContext != null)
|
||||
throw new ArtemisCoreException($"{nameof(SkiaGraphicsContext)} can only be set once.");
|
||||
_skiaGraphicsContext = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,12 +193,12 @@ namespace Artemis.Core
|
||||
{
|
||||
canvas.Save();
|
||||
Renderer.Open(Path, Parent as Folder);
|
||||
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
|
||||
if (Renderer.Surface == null || Renderer.Path == null || Renderer.Paint == null)
|
||||
throw new ArtemisCoreException("Failed to open folder render context");
|
||||
|
||||
SKRect rendererBounds = Renderer.Path.Bounds;
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
baseLayerEffect.PreProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
||||
baseLayerEffect.PreProcess(Renderer.Surface.Canvas, rendererBounds, Renderer.Paint);
|
||||
|
||||
// If required, apply the opacity override of the module to the root folder
|
||||
if (IsRootFolder && Profile.Module.OpacityOverride < 1)
|
||||
@@ -213,12 +213,12 @@ namespace Artemis.Core
|
||||
|
||||
// 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--)
|
||||
Children[index].Render(Renderer.Canvas);
|
||||
Children[index].Render(Renderer.Surface.Canvas);
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
baseLayerEffect.PostProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
||||
baseLayerEffect.PostProcess(Renderer.Surface.Canvas, rendererBounds, Renderer.Paint);
|
||||
|
||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
||||
canvas.DrawSurface(Renderer.Surface, Renderer.TargetLocation, Renderer.Paint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -330,8 +330,7 @@ namespace Artemis.Core
|
||||
{
|
||||
canvas.Save();
|
||||
Renderer.Open(Path, Parent as Folder);
|
||||
|
||||
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
|
||||
if (Renderer.Surface == null || Renderer.Path == null || Renderer.Paint == null)
|
||||
throw new ArtemisCoreException("Failed to open layer render context");
|
||||
|
||||
// Apply blend mode and color
|
||||
@@ -357,11 +356,11 @@ namespace Artemis.Core
|
||||
if (LayerBrush.SupportsTransformation)
|
||||
{
|
||||
SKMatrix rotationMatrix = GetTransformMatrix(true, false, false, true);
|
||||
Renderer.Canvas.SetMatrix(Renderer.Canvas.TotalMatrix.PreConcat(rotationMatrix));
|
||||
Renderer.Surface.Canvas.SetMatrix(Renderer.Surface.Canvas.TotalMatrix.PreConcat(rotationMatrix));
|
||||
}
|
||||
|
||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
||||
Renderer.Canvas.ClipPath(renderPath);
|
||||
Renderer.Surface.Canvas.ClipPath(renderPath);
|
||||
DelegateRendering(renderPath.Bounds);
|
||||
}
|
||||
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
|
||||
@@ -370,11 +369,11 @@ namespace Artemis.Core
|
||||
renderPath.Transform(renderPathMatrix);
|
||||
|
||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
||||
Renderer.Canvas.ClipPath(renderPath);
|
||||
Renderer.Surface.Canvas.ClipPath(renderPath);
|
||||
DelegateRendering(Renderer.Path.Bounds);
|
||||
}
|
||||
|
||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
||||
canvas.DrawSurface(Renderer.Surface, Renderer.TargetLocation, Renderer.Paint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -395,16 +394,16 @@ namespace Artemis.Core
|
||||
{
|
||||
if (LayerBrush == null)
|
||||
throw new ArtemisCoreException("The layer is not yet ready for rendering");
|
||||
if (Renderer.Canvas == null || Renderer.Paint == null)
|
||||
if (Renderer.Surface == null || Renderer.Paint == null)
|
||||
throw new ArtemisCoreException("Failed to open layer render context");
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
baseLayerEffect.PreProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
||||
baseLayerEffect.PreProcess(Renderer.Surface.Canvas, bounds, Renderer.Paint);
|
||||
|
||||
LayerBrush.InternalRender(Renderer.Canvas, bounds, Renderer.Paint);
|
||||
LayerBrush.InternalRender(Renderer.Surface.Canvas, bounds, Renderer.Paint);
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
baseLayerEffect.PostProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
||||
baseLayerEffect.PostProcess(Renderer.Surface.Canvas, bounds, Renderer.Paint);
|
||||
}
|
||||
|
||||
internal void CalculateRenderProperties()
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace Artemis.Core
|
||||
private bool _disposed;
|
||||
private SKRect _lastBounds;
|
||||
private SKRect _lastParentBounds;
|
||||
public SKBitmap? Bitmap { get; private set; }
|
||||
public SKCanvas? Canvas { get; private set; }
|
||||
public SKSurface? Surface { get; private set; }
|
||||
public SKPaint? Paint { get; private set; }
|
||||
public SKPath? Path { get; private set; }
|
||||
public SKPoint TargetLocation { get; private set; }
|
||||
@@ -31,22 +30,26 @@ namespace Artemis.Core
|
||||
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds))
|
||||
Invalidate();
|
||||
|
||||
if (!_valid || Canvas == null)
|
||||
if (!_valid || Surface == null)
|
||||
{
|
||||
SKRect pathBounds = path.Bounds;
|
||||
int width = (int) pathBounds.Width;
|
||||
int height = (int) pathBounds.Height;
|
||||
|
||||
Bitmap = new SKBitmap(width, height);
|
||||
SKImageInfo imageInfo = new SKImageInfo(width, height);
|
||||
if (Constants.SkiaGraphicsContext == null)
|
||||
Surface = SKSurface.Create(imageInfo);
|
||||
else
|
||||
Surface = SKSurface.Create(Constants.SkiaGraphicsContext, true, imageInfo);
|
||||
|
||||
Path = new SKPath(path);
|
||||
Canvas = new SKCanvas(Bitmap);
|
||||
Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
|
||||
|
||||
TargetLocation = new SKPoint(pathBounds.Location.X, pathBounds.Location.Y);
|
||||
if (parent != null)
|
||||
TargetLocation -= parent.Bounds.Location;
|
||||
|
||||
Canvas.ClipPath(Path);
|
||||
Surface.Canvas.ClipPath(Path);
|
||||
|
||||
_lastParentBounds = parent?.Bounds ?? new SKRect();
|
||||
_lastBounds = path.Bounds;
|
||||
@@ -55,8 +58,8 @@ namespace Artemis.Core
|
||||
|
||||
Paint = new SKPaint();
|
||||
|
||||
Canvas.Clear();
|
||||
Canvas.Save();
|
||||
Surface.Canvas.Clear();
|
||||
Surface.Canvas.Save();
|
||||
|
||||
IsOpen = true;
|
||||
}
|
||||
@@ -66,7 +69,7 @@ namespace Artemis.Core
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("Renderer");
|
||||
|
||||
Canvas?.Restore();
|
||||
Surface?.Canvas.Restore();
|
||||
Paint?.Dispose();
|
||||
Paint = null;
|
||||
|
||||
@@ -86,15 +89,13 @@ namespace Artemis.Core
|
||||
if (IsOpen)
|
||||
Close();
|
||||
|
||||
Canvas?.Dispose();
|
||||
Surface?.Dispose();
|
||||
Paint?.Dispose();
|
||||
Path?.Dispose();
|
||||
Bitmap?.Dispose();
|
||||
|
||||
Canvas = null;
|
||||
Surface = null;
|
||||
Paint = null;
|
||||
Path = null;
|
||||
Bitmap = null;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Presets.Textures.Sampler;
|
||||
using SkiaSharp;
|
||||
@@ -10,14 +12,19 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private SKPixmap? _pixelData;
|
||||
private SKImage? _rasterImage;
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal SKTexture(int width, int height, float renderScale)
|
||||
: base(width, height, 4, new AverageByteSampler())
|
||||
{
|
||||
Bitmap = new SKBitmap(new SKImageInfo(width, height, SKColorType.Rgb888x));
|
||||
ImageInfo = new SKImageInfo(width, height);
|
||||
if (Constants.SkiaGraphicsContext == null)
|
||||
Surface = SKSurface.Create(ImageInfo);
|
||||
else
|
||||
Surface = SKSurface.Create(Constants.SkiaGraphicsContext, true, ImageInfo);
|
||||
RenderScale = renderScale;
|
||||
}
|
||||
|
||||
@@ -25,10 +32,20 @@ namespace Artemis.Core
|
||||
|
||||
#region Methods
|
||||
|
||||
internal void CopyPixelData()
|
||||
{
|
||||
using SKImage skImage = Surface.Snapshot();
|
||||
|
||||
_rasterImage?.Dispose();
|
||||
_pixelData?.Dispose();
|
||||
_rasterImage = skImage.ToRasterImage();
|
||||
_pixelData = _rasterImage.PeekPixels();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
@@ -38,12 +55,17 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets the SKBitmap backing this texture
|
||||
/// </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>
|
||||
/// Gets the color data in RGB format
|
||||
/// </summary>
|
||||
protected override ReadOnlySpan<byte> Data => _disposed ? new ReadOnlySpan<byte>() : Bitmap.GetPixelSpan();
|
||||
protected override ReadOnlySpan<byte> Data => _pixelData != null ? _pixelData.GetPixelSpan() : ReadOnlySpan<byte>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the render scale of the texture
|
||||
@@ -63,12 +85,12 @@ namespace Artemis.Core
|
||||
{
|
||||
IsInvalid = true;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
Bitmap.Dispose();
|
||||
Surface.Dispose();
|
||||
_pixelData?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -139,21 +139,23 @@ namespace Artemis.Core.Services
|
||||
module.InternalUpdate(args.DeltaTime);
|
||||
|
||||
// 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);
|
||||
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)
|
||||
{
|
||||
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));
|
||||
foreach (Module module in modules.Where(m => m.IsActivated))
|
||||
module.InternalRender(args.DeltaTime, canvas, texture.ImageInfo);
|
||||
}
|
||||
|
||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
||||
canvas.RestoreToCount(-1);
|
||||
canvas.Flush();
|
||||
|
||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using Artemis.Storage.Entities.Surface;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using RGB.NET.Core;
|
||||
using Serilog;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
@@ -52,7 +51,7 @@ namespace Artemis.Core.Services
|
||||
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
|
||||
Surface.RegisterUpdateTrigger(UpdateTrigger);
|
||||
}
|
||||
|
||||
|
||||
public TimerUpdateTrigger UpdateTrigger { get; }
|
||||
|
||||
protected virtual void OnDeviceRemoved(DeviceEventArgs e)
|
||||
@@ -232,8 +231,9 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
if (!RenderOpen)
|
||||
throw new ArtemisCoreException("Render pipeline is already closed");
|
||||
|
||||
|
||||
RenderOpen = false;
|
||||
_texture?.CopyPixelData();
|
||||
}
|
||||
|
||||
public void CreateTexture()
|
||||
|
||||
Reference in new issue
Block a user