mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-09 18:36:27 +00:00
Merge pull request #906 from Artemis-RGB/feature/small-led-render-scale
Keep small LEDs addressable at reduced render scales and reduce texture readback allocations
This commit is contained in:
2 files changed
+75
-16
No files matched your search
@@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Artemis.Core.SkiaSharp;
|
using Artemis.Core.SkiaSharp;
|
||||||
using HPPH;
|
using HPPH;
|
||||||
using HPPH.SkiaSharp;
|
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using RGB.NET.Presets.Extensions;
|
using RGB.NET.Presets.Extensions;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@@ -25,33 +24,85 @@ public sealed class SKTexture : ITexture, IDisposable
|
|||||||
Surface = graphicsContext == null
|
Surface = graphicsContext == null
|
||||||
? SKSurface.Create(ImageInfo)
|
? SKSurface.Create(ImageInfo)
|
||||||
: SKSurface.Create(graphicsContext.GraphicsContext, true, ImageInfo);
|
: SKSurface.Create(graphicsContext.GraphicsContext, true, ImageInfo);
|
||||||
|
_readback = new SKBitmap(new SKImageInfo(width, height, SKColorType.Bgra8888, SKAlphaType.Unpremul));
|
||||||
|
|
||||||
foreach (ArtemisDevice artemisDevice in devices)
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
{
|
{
|
||||||
foreach (ArtemisLed artemisLed in artemisDevice.Leds)
|
foreach (ArtemisLed artemisLed in artemisDevice.Leds)
|
||||||
{
|
_ledRects[artemisLed.RgbLed] = CreateSampleRect(artemisLed.RgbLed.AbsoluteBoundary, scale, width, height);
|
||||||
_ledRects[artemisLed.RgbLed] = SKRectI.Create(
|
|
||||||
(int)(artemisLed.AbsoluteRectangle.Left * RenderScale),
|
|
||||||
(int)(artemisLed.AbsoluteRectangle.Top * RenderScale),
|
|
||||||
(int)(artemisLed.AbsoluteRectangle.Width * RenderScale),
|
|
||||||
(int)(artemisLed.AbsoluteRectangle.Height * RenderScale)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private static SKRectI CreateSampleRect(Rectangle boundary, float scale, int width, int height)
|
||||||
|
{
|
||||||
|
(int left, int right) = ScaleSpan(boundary.Location.X, boundary.Size.Width, scale, width);
|
||||||
|
(int top, int bottom) = ScaleSpan(boundary.Location.Y, boundary.Size.Height, scale, height);
|
||||||
|
return new SKRectI(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (int Start, int End) ScaleSpan(float location, float size, float scale, int max)
|
||||||
|
{
|
||||||
|
float start = location * scale;
|
||||||
|
float end = (location + size) * scale;
|
||||||
|
if (end <= 0 || start >= max)
|
||||||
|
return (0, 0);
|
||||||
|
|
||||||
|
// Always sample at least one pixel
|
||||||
|
int startPixel = Math.Clamp(start.RoundToInt(), 0, max - 1);
|
||||||
|
int endPixel = Math.Clamp(end.RoundToInt(), startPixel + 1, max);
|
||||||
|
return (startPixel, endPixel);
|
||||||
|
}
|
||||||
|
|
||||||
internal Color GetColorAtRenderTarget(in RenderTarget renderTarget)
|
internal Color GetColorAtRenderTarget(in RenderTarget renderTarget)
|
||||||
{
|
{
|
||||||
if (_image == null) return Color.Transparent;
|
if (!_hasPixels) return Color.Transparent;
|
||||||
|
|
||||||
SKRectI skRectI = _ledRects[renderTarget.Led];
|
SKRectI skRectI = _ledRects[renderTarget.Led];
|
||||||
|
|
||||||
if (skRectI.Width <= 0 || skRectI.Height <= 0)
|
if (skRectI.Width <= 0 || skRectI.Height <= 0)
|
||||||
return Color.Transparent;
|
return Color.Transparent;
|
||||||
|
|
||||||
return _image[skRectI.Left, skRectI.Top, skRectI.Width, skRectI.Height].Average().ToColor();
|
return AveragePixels(_readback, skRectI);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static unsafe Color AveragePixels(SKBitmap bitmap, SKRectI rectangle)
|
||||||
|
{
|
||||||
|
int left = Math.Max(0, rectangle.Left);
|
||||||
|
int top = Math.Max(0, rectangle.Top);
|
||||||
|
int right = Math.Min(bitmap.Width, rectangle.Right);
|
||||||
|
int bottom = Math.Min(bitmap.Height, rectangle.Bottom);
|
||||||
|
if (left >= right || top >= bottom)
|
||||||
|
return Color.Transparent;
|
||||||
|
|
||||||
|
byte* pixels = (byte*)bitmap.GetPixels().ToPointer();
|
||||||
|
if (pixels == null)
|
||||||
|
return Color.Transparent;
|
||||||
|
|
||||||
|
long blue = 0;
|
||||||
|
long green = 0;
|
||||||
|
long red = 0;
|
||||||
|
long alpha = 0;
|
||||||
|
int rowBytes = bitmap.RowBytes;
|
||||||
|
for (int y = top; y < bottom; y++)
|
||||||
|
{
|
||||||
|
byte* pixel = pixels + y * rowBytes + left * 4;
|
||||||
|
for (int x = left; x < right; x++, pixel += 4)
|
||||||
|
{
|
||||||
|
blue += pixel[0];
|
||||||
|
green += pixel[1];
|
||||||
|
red += pixel[2];
|
||||||
|
alpha += pixel[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float count = (right - left) * (bottom - top);
|
||||||
|
return new ColorBGRA(
|
||||||
|
(byte)MathF.Round(blue / count),
|
||||||
|
(byte)MathF.Round(green / count),
|
||||||
|
(byte)MathF.Round(red / count),
|
||||||
|
(byte)MathF.Round(alpha / count)).ToColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -63,6 +114,8 @@ public sealed class SKTexture : ITexture, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_hasPixels = false;
|
||||||
|
_readback.Dispose();
|
||||||
Surface.Dispose();
|
Surface.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
@@ -79,15 +132,15 @@ public sealed class SKTexture : ITexture, IDisposable
|
|||||||
|
|
||||||
internal void CopyPixelData()
|
internal void CopyPixelData()
|
||||||
{
|
{
|
||||||
using SKImage skImage = Surface.Snapshot();
|
_hasPixels = Surface.ReadPixels(_readback.Info, _readback.GetPixels(), _readback.RowBytes, 0, 0);
|
||||||
_image = skImage.ToImage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties & Fields
|
#region Properties & Fields
|
||||||
|
|
||||||
private IImage? _image;
|
private readonly SKBitmap _readback;
|
||||||
|
private bool _hasPixels;
|
||||||
private readonly Dictionary<Led, SKRectI> _ledRects = [];
|
private readonly Dictionary<Led, SKRectI> _ledRects = [];
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -119,4 +172,4 @@ public sealed class SKTexture : ITexture, IDisposable
|
|||||||
public bool IsInvalid { get; private set; }
|
public bool IsInvalid { get; private set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -47,6 +47,7 @@ internal class RenderService : IRenderService, IRenderer, IDisposable
|
|||||||
_preferredGraphicsContext.SettingChanged += PreferredGraphicsContextOnSettingChanged;
|
_preferredGraphicsContext.SettingChanged += PreferredGraphicsContextOnSettingChanged;
|
||||||
|
|
||||||
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||||
|
ApplyRenderScaleMultiplier();
|
||||||
_surfaceManager = new SurfaceManager(this, GraphicsContext, _targetFrameRateSetting.Value, (float) _renderScaleSetting.Value);
|
_surfaceManager = new SurfaceManager(this, GraphicsContext, _targetFrameRateSetting.Value, (float) _renderScaleSetting.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,9 +190,14 @@ internal class RenderService : IRenderService, IRenderer, IDisposable
|
|||||||
_surfaceManager.UpdateTargetFrameRate(_targetFrameRateSetting.Value);
|
_surfaceManager.UpdateTargetFrameRate(_targetFrameRateSetting.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
private void ApplyRenderScaleMultiplier()
|
||||||
{
|
{
|
||||||
RenderScale.SetRenderScaleMultiplier((int) (1 / _renderScaleSetting.Value));
|
RenderScale.SetRenderScaleMultiplier((int) (1 / _renderScaleSetting.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ApplyRenderScaleMultiplier();
|
||||||
_surfaceManager.UpdateRenderScale((float) _renderScaleSetting.Value);
|
_surfaceManager.UpdateRenderScale((float) _renderScaleSetting.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user