-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
How to render SKCanvas with DrawingContext? [Question] #2492
Comments
If you want to have "SKBitmap-backed" control, you can use #2371 allows you to add a node directly to Avalonia's scene graph. Scene graph nodes are rendered on a separate thread. For a drawing app, WriteableBitmap is recommended. |
So i don't mind using WriteableBitmap, but unsure how to write to it. Is there an abstracted api that can easily allow drawing operations on it similar to SkiaCanvas? Or am I just doing something wrong? Below is my first attempt based on casting to SKBitmap, but I get the exception below.
My assumption is the backend is something other than Skia or it is on the GPU and won't cast neatly to a SKBitmap. Unsure how to proceed.
Edit: Looking into the source it appears this is intentionally done like this. The BitmapFramebuffer class is a private class that wraps the SKBitmap when you Lock it. BitmapFramebuffer even if public abstracts away the SKBitmap class by making the SKBitmap member private also. Recompiling and making these members public could allow for interop but I don't think that is necessarily reliable if the backend shifts away from Skia. I'm not familiar enough with the project to know if that is a valid concern since it is in beta. |
You can create a new skia surface from var info = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
framebuffer.Format.ToSkColorType(),SKAlphaType.Premul);
using(var surface = SKSurface.Create(info, framebuffer.Address, framebuffer.RowBytes))
{
var canvas = surface.Canvas;
} |
Cool, I have it working with the below code. 1 thing is I turned on FPS and drawing the line it drops down to 15-40 fps when drawing. This FPS drop results in a noticeable lag to the line when drawing. Is there a better way to do the drawing that comes to mind? It seems like that is pretty hefty performance penalty for such a simple operation. An HTML Canvas performs better in comparison.
|
In my opinion we should change SurfaceRenderTarget to expose the underlying SKCanvas. That should yield decent performance. Using WritableBitmap is always a performance hit. |
I have better performance when using SkiaSharp then when using DrawingContext. @ChaseLewis This is my app, its a bit complex but its drawing app and is using SkiaSharp |
Thanks @wieslawsoltes going to take a second to understand everything your doing here. Seems your using a custom renderer and then grabbing the SKCanvas directly from that renderer so you never have to write to WriteableBitmap. I did get 47-50 FPS though by adding the use GPU lines to my Program.cs that I saw in your app. Still not perfect as I'd expect it to handle 59-60 FPS range pretty well with such a simple operation but that is fairly passable for the second.
Even at 60 FPS drawing apps often have issues with keeping up completely for a smooth experience. Microsoft did a study a long time ago and found it took nearly 125 FPS before users couldn't tell any lag when drawing on an electronic surface. I'm just trying to make it a passable experience. DrawingCanvas type controls are pretty common in most frameworks and all the stuff seems to exist under the hood already if the SKCanvas of the render target was exposed. |
I prefer the RenderTargetBitmap approach because it doesn't effect the whole scene. If you expose the window's surface there is a lot that can go wrong. |
You need to also install https://www.nuget.org/packages/Avalonia.Angle.Windows.Natives/ to actually enable GPU acceleration. "Custom drawing operations" is probably the fastest thing you can use right now, but it ticks on the render thread. It should be possible to obtain hw-accelerated SkCanvas from
It's already possible to get SkCanvas from any skia-backed drawing context. |
I know that you can get the SKCanvas from drawing context I just dont't like the fact that you can ruin the whole rendering of a window when you do something wrong. For example, you can draw outside of the actual control with that approach.
|
You can already do this: var bitmap = new RenderTargetBitmap()
using(var ctx = bitmap.CreateDrawingContext(null))
{
var canvas = ((ISkiaDrawingContextImpl)renderTarget).SkCanvas;
} |
Yes true. But this is not limited to a DrawingContext that comes from RenderTargetBitmap and in my opinion, it should be that way. If you want to use the window's SKCanvas you should create your window with a special surface that exposes that canvas. That way it is always clear where the canvas comes from. No side effects etc. |
So the performance using the RenderTargetBitmap is FAAAR better. Doesn't drop even a single frame. One note though is you need to get 0.8.1 cli build to get access to the ISkiaDrawingContextImpl. It is not available in the currently stable 0.8.0 version. That took me a second to figure out. |
Example code for anyone that it may help
|
FYI in 0.8 you can also get SKCanvas with ((DrawingContextImpl)tgtBitmap).Canvas |
This is so much faster then using custom drawing with skia. |
@ChaseLewis Why do you use |
@weislawsoltes The thought of using shaders gives the possibility of having individual tools have custom shaders eventually. The shader interop has more potential for being flexible with the ITool paradigm I'm shooting for. Eventually I'd like effect layers and such. Imagine a tool when you draw that looks like it's unveiling a texture even though you don't have any masking, blur tool, etc. Kinda future proofing for some ideas I had more than anything. |
@ChaseLewis Thanks for the reply 😄 The shaders are are indeed nice https://docs.microsoft.com/en-us/dotnet/api/skiasharp.skshader?view=skiasharp-1.68.0 |
this doesn't work anymore |
@Shadowblitz16 you need ICustomDrawOperation API |
Why is the imaging api so convoluted? |
You need to call Custom on the DrawingContext with your ICustomDrawingOperation implementation. A custom drawing operation is required because actual drawing isn't happening immediately. Therefore you don't get access to the actual SKCanvas otherwise |
I'm interested in trying to make a drawing app with Avalonia. Pretty impressed with modest offerings so far. To do so I'd like to manage the layers and SKCanvas myself since functionality at that level seems overall easier to use for what i'm trying to do. I can manage the bitmap layers, bitmaps, etc myself. Redrawing
I found #2371 however, I don't 100% understand how to convert the SKCanvas or SKBitmap object into something Avalonia can consume for drawing. Anyone able to explain how one would consume operations from SkiaSharp and present them in a user control? I assume I have to draw the underlying SKBitmap in the UserControl.Render method, but not sure how to do so.
The text was updated successfully, but these errors were encountered: