Reduce per-frame texture readback allocations

This commit is contained in:
rjc862003 authored and RobertBeekman committed 2026-10-17 20:38:30 +02:00
1 parent e1357f1bf0
commit 02dd7df5dd
1 file changed
+46 -7
+46 -7
View File
@@ -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,6 +24,7 @@ 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)
{ {
@@ -57,14 +57,52 @@ public sealed class SKTexture : ITexture, IDisposable
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 />
@@ -76,6 +114,7 @@ public sealed class SKTexture : ITexture, IDisposable
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_readback.Dispose();
Surface.Dispose(); Surface.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@@ -92,15 +131,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 />
@@ -132,4 +171,4 @@ public sealed class SKTexture : ITexture, IDisposable
public bool IsInvalid { get; private set; } public bool IsInvalid { get; private set; }
#endregion #endregion
} }