mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.14k stars 522 forks source link

[BUG] SKCanvasView's PaintSurface is being called even when hidden from view upon invalidating surface of another SKCanvasView #2813

Closed janne-hmp closed 3 weeks ago

janne-hmp commented 1 month ago

Description

I'm working with a game, GnollHack, that uses several SKCanvasView elements. Currently we are porting it to .NET MAUI using .NET 9.0 Preview 2 and SkiaSharp 3.0 Preview 2.1 (other versions of MAUI and SkiaSharp are pretty hopeless). I have a few of SKCanvasViews on start screen (MainPage.xaml) and then one such canvas on GamePage (which opens in the navigation stack on the top of the MainPage): MainPage has SKCanvasView inside a custom control called ImageCarousel and GamePage inside a custom control called SwitchableCanvasView that can be toggled to use either normal SKCanvasView or SKGLView. Both MainPage and GamePage have their own animation timers to update the relevant SKCanvasViews. When I start the game, and GamePage goes on the top of the navigation stack, animation timer on MainPage is stopped, and the SKCanvasView should not update anymore to save resources, also since the whole page is hidden under the GamePage. This works in Xamarin, which does not update anything on MainPage when it is not visible, but in .NET MAUI all SKCanvasViews on MainPage (hidden below the GamePage in the navigation stack) get updated (resulting in a call to PaintSurface) every time SwitchableCanvasView's InvalidateSurface is being called (60 times per second).

This results in noticeable performance degradation, because unnecessary drawing operations on large bitmaps are being called on a page that is hidden from view. In Xamarin, all of this works as intended, and only SwitchableCanvasView on GamePage gets updated. In any case, since nothing new is being drawn on MainPage, its SKCanvasViews should either (1) realise that they are not visible and do nothing or (2) use cached images.

This may be related to an earlier problem in .NET 8.0 (fixed in .NET 9.0) where a SKCanvasView drew a grey box instead of intended graphics when being disabled or when the page started disappear. If so, perhaps some additional work is needed to make sure that a cached image is being used when the control is not visible (and drawing the graphics only once if there is no cached image, and saving that image to cache).

Code

https://github.com/hyvanmielenpelit/GnollHack

Expected Behavior

PaintSurface method of a hidden SKCanvasView is NOT called when another, visible SKCanvasView draws itself via PaintSurface (due to an earlier InvalidateSurface call on it and not on the hidden SKCanvasView).

Actual Behavior

PaintSurface method of a hidden SKCanvasView is called when another, visible SKCanvasView draws itself via PaintSurface (due to an earlier InvalidateSurface call on it and not on the hidden SKCanvasView).

Version of SkiaSharp

3.x (Alpha)

Last Known Good Version of SkiaSharp

2.80.x (Deprecated)

IDE / Editor

Visual Studio (Windows)

Platform / Operating System

Android, iOS

Platform / Operating System Version

Android 14, iOS 17.3.1

Devices

Google Pixel 6a, iPad 11,7

Relevant Screenshots

No response

Relevant Log Output

No response

Code of Conduct

janne-hmp commented 1 month ago

@mattleibow As far as I can tell, all SKCanvasViews get updated (OnPaintSurface is called) each time one of them gets invalidated / updated. This is a pretty big performance hog in .NET MAUI compared to Xamarin, so I would prioritize fixing this, if possible. Currently, I have to run separate checks at the start of OnPaintSurface of each SKCanvasView in .NET MAUI to determine whether the control should actually be redrawn or not. For some custom controls, it is difficult to tell if they should be redrawn or not, so the result is a meaningful performance degradation from Xamarin to .NET MAUI.

mattleibow commented 1 month ago

The sample is quite large, are you able to create a smaller sample for me to check? Typically the invalidation is asking the view to update, so there should be no cross-invalidation. Each view is separate and in no way linked to others.

janne-hmp commented 1 month ago

Let me investigate this further myself, too. I checked yesterday that some of the other SKCanvases in other custom controls in fact do not update themselves, so perhaps this is something very specific to the setup. It is just a few custom controls named "CustomLabel" on the main screen when the page is hidden under another page. I'll see if I can reproduce this in a smaller sample project, and will come back to you a bit later.

janne-hmp commented 3 weeks ago

Seems like this is not happening anymore, so closing this for now.