New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Full C# refactor: move to .NET 6, clean code, add NuGet packaging, more examples, efficient APIs #1513
Conversation
It remains only to publish the NuGet package (this needs to be discussed with @hzeller), then add information about it to the README. |
Implemented the
I am not a professional in C++, and maybe something can be done better. |
I don't know anything about c#, so I'll pas this to @digital-synapse who contributed the original c# binding to comment and review. |
Can you add the option to set the drop privileges argument? |
Oh, and also I need someone to review the code, as I don't know anything about best practices in the C# environment. |
Sorry for the long delay! I have reviewed the changes and they look fine but they break support on raspberry pi 0 and 0w. (This is the reason I implemented with mono for broader support in the first place even though I would have preferred .net core. See dotnet issue here dotnet/iot#998) Anyways, can we please move these changes to a separate binding instead of changing the existing mono binding. That will preserve compatibility for rpi zeros. Thanks! |
You can install .NET 6 on a Raspberry Pi zero : https://www.nikouusitalo.com/blog/installing-net-6-on-a-raspberry-pi-zero-2-w/ |
Oh that's good. That wasn't previously possible. Perhaps just .net core is an issue then. Ok to merge. |
I have been trying to compile using the mono binding, but it has been a lot of hassle for some reason. This would be a great addition! 👍 |
Based on this i wrote App for my small 64x32 Matrix AND for faster development i build a LedMatrixSimulator. This will help c# development with this library. |
Nice! Will definitely look into it if I find some spare time 😀 Time is tickin' so fast, it's really time to dig into this stuff again... btw. I have started to write a simple "Canvas-Manager" for this library a few years ago. It supports an unlimited amount of layers, i.e. for scrolling text, media playback and so on...it's far from final but maybe it's worth sharing. |
@KirillAldashkin I throw an SKBitmap using direct memory pointers to the matrix, which is the fastest method I know of, please see the code using your new method: private void DrawBitmapPixelsToCanvas(RGBLedMatrix ledMatrix, RGBLedCanvas canvas, SKBitmap bitmap)
{
var colors = new List<Color>();
var pixelAddr = bitmap.GetPixels();
unsafe
{
var ptr = (byte*)pixelAddr.ToPointer();
for (var col = 0; col < bitmap.Height; col++)
for (var row = 0; row < bitmap.Width; row++)
{
var b = *ptr++;
var g = *ptr++;
var r = *ptr++;
ptr++; //Skip alpha
//canvas.SetPixel(row, col, new Color(r, g, b));
colors.Add(new Color(b, g, r));
}
}
canvas.SetPixels(0, 0, bitmap.Width, bitmap.Height, colors.ToArray());
ledMatrix.SwapOnVsync(canvas);
} |
@Jan1503 Some sketch from the phone without any copying: private unsafe void DrawBitmapPixelsToCanvas(RGBLedMatrix ledMatrix, RGBLedCanvas canvas, SKBitmap bitmap)
{
var ptr = (Color*)bitmap.GetPixels();
var span = new Span<Color>(ptr, bitmap.Width * bitmap.Height); //wraps a specified region of memory without any copying
canvas.SetPixels(0, 0, bitmap.Width, bitmap.Height, span);
ledMatrix.SwapOnVsync(canvas);
} |
@KirillAldashkin |
After a second view I don't think this will work ootb cause you ignore skipping the Alpha value of the SKBitmap pixel. AfaIk Skia does not support 24-bit pixel format so you have to deal with the alpha value. |
@Jan1503 private unsafe void DrawBitmapPixelsToCanvas(RGBLedMatrix ledMatrix, RGBLedCanvas canvas, SKBitmap bitmap)
{
//allocate buffer on stack, will work only with small image, otherwise
//use ArrayPool<Color>.Shared.Rent() and ArrayPool<Color>.Shared.Return()
Span<Color> data = stackalloc Color[bitmap.Width * bitmap.Height];
var ptr = (int*)bitmap.GetPixels();
for (var i = 0; i < bitmap.Height * bitmap.Width; i++)
{
data[i] = *(Color*)ptr;
ptr++;
}
canvas.SetPixels(0, 0, bitmap.Width, bitmap.Height, data);
ledMatrix.SwapOnVsync(canvas);
} But I would just use |
@KirillAldashkin |
@Jan1503 |
@KirillAldashkin But you're right, let's discuss in your repo. Edit: Just tried the method above but the index "i" is not declared. Would you mind fixing it? |
The bottleneck will always be the gpio, bus frequency, and the CPU imho.
Any graphics processing as much as possible should be performed off CPU (on
the GPU) and the frame buffer copied to memory. Following this approach, I
was able to render realtime shadertoy pixel shaders on a 128x64 RGB led
panel at a consistent 30fps with the mono implementation. (On raspberry pi
zero)
…On Sat, May 27, 2023, 2:33 AM Jan Wrage ***@***.***> wrote:
@KirillAldashkin <https://github.com/KirillAldashkin>
This seems to be the explanation for working with pixel buffers:
<https://docs.sixlabors.com/articles/imagesharp/pixelbuffers.html>
But you're right, let's discuss in your repo.
—
Reply to this email directly, view it on GitHub
<#1513 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AB6YU7JNUAHSNQY7SPZEG5LXIHC7FANCNFSM6AAAAAAUZC4TIA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
The code looks good to me, and is very similar to my own implementation where the only major difference is the addition of interfaces for the canvas, font and matrix so I could replace them for testing. Possibly out of scope for this PR, but there are a couple of possible useful additional functions:
I can do the C# parts but c / c++ is well out of my comfort zone. I have some others I have already implemented and will look at getting them into shape and adding them once this PR is merged. |
@UniqueDaniel I don't think that such APIs are needed because:
|
Just for a quick comparison I switched my CanvasManager to ImageSharp and it's about 20-30 fps slower than Skia, mostly because of the pure managed implementation I think. This is my implementation using Skia (thanks @KirillAldashkin for optimization, though it not seems faster ;)): private unsafe void DrawBitmapPixelsToCanvas(RGBLedMatrix ledMatrix, RGBLedCanvas canvas, SKBitmap bitmap)
{
Span<Color> data = stackalloc Color[bitmap.Width * bitmap.Height];
var ptr = (int*)bitmap.GetPixels();
for (var col = 0; col < bitmap.Height; col++)
for (var row = 0; row < bitmap.Width; row++)
{
data[row + (col * bitmap.Width)] = *(Color*)ptr;
ptr++;
}
canvas.SetPixels(0, 0, bitmap.Width, bitmap.Height, data);
ledMatrix.SwapOnVsync(canvas);
} and this is ImageSharp: private void DrawBitmapPixelsToCanvasImageSharp(RGBLedMatrix ledMatrix, RGBLedCanvas canvas, Image<Rgb24> img)
{
//Configuration customConfig = Configuration.Default.Clone();
//customConfig.PreferContiguousImageBuffers = true;
{
if (!img.DangerousTryGetSinglePixelMemory(out Memory<Rgb24> memory))
{
throw new Exception(
"This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true.");
}
using MemoryHandle pinHandle = memory.Pin();
unsafe
{
var ptr = (Color*)pinHandle.Pointer;
var span = new Span<Color>(ptr, img.Width * img.Height);
canvas.SetPixels(0, 0, img.Width, img.Height, span);
}
}
ledMatrix.SwapOnVsync(canvas);
} |
I meant that one @Jan1503 private void DrawBitmapPixelsToCanvasImageSharp(RGBLedMatrix ledMatrix, RGBLedCanvas canvas, Image<Rgb24> img)
{
if (!img.DangerousTryGetSinglePixelMemory(out Memory<Rgb24> memory))
throw new Exception("This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true.");
canvas.SetPixels(0, 0, img.Width, img.Height, MemoryMarshal.Cast<Rgb24, Color>(memory.Span));
ledMatrix.SwapOnVsync(canvas);
} (and |
@KirillAldashkin |
Alright, looks like things are settled so far. @digital-synapse is fine with merging according to this comment. If there are remaining things to work on, please just open a new pull request. |
Currenty, C# bindings are built using Mono, which is a completely obsolete toolchain in the .NET ecosystem.
This PR is aimed at updating C# bindings to use modern and well-known .NET tools and do necessary and relevant tasks, such as:
Tasks:
.csproj
NuGet
packagingREADME
, etc.)This will bring the following benefits: