SkiaSharp icon indicating copy to clipboard operation
SkiaSharp copied to clipboard

[BUG] [WinUI] Scale delay is observed during window resize

Open APopatanasov opened this issue 2 years ago • 10 comments
trafficstars

Description When the window of WinUI application (that has SKCanvasView inside it) is constantly resized the scale of the canvas drawing does not look accurate for some of the frames. That leaves the feeling that the canvas is somehow flickering. The issue can be reproduced with every drawing in the canvas, but it is easily reproduced when you have multiple text drawings on the screen.

After a brief research I've found that the issues was caused by this PR. And more specifically by the following code change: Screenshot 2022-12-15 114944

I can suggest two possible solutions for this issue:

  1. You can bring back the Scale Transform and set the Stretch to None.
  2. Or you can invoke DoInvalidate instead of Invalidate from OnSizeChanged. Invalidate invokes DoInvalidate, but from within a dispatcher which causes the inaccurate rendering of the frames. SizeChanged should be invoked from within the UI thread, so it is safe to invoke DoInvalidate instead of Invalidate.

Code

using SkiaSharp;
using SkiaSharp.Views.Maui;
using SkiaSharp.Views.Maui.Controls;

namespace SkiaScaleWinUI;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();

        MainPage = new TextPage();
    }

    public class TextPage : ContentPage
    {
        public TextPage()
        {
            Title = "Framed Text";

            SKCanvasView canvasView = new SKCanvasView();
            canvasView.PaintSurface += OnCanvasViewPaintSurface;
            Content = canvasView;
        }

        void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
        {
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;

            canvas.Clear();

            string str = "Hello SkiaSharp!";
            SKPaint textPaint = new SKPaint { Color = SKColors.Chocolate, TextSize = 24 };

            canvas.DrawText(str, 0, args.Info.Height / 2, textPaint);
        }
    }
}

Expected Behavior

During windows resize the text should not be scaled - at the size and the DPI should be taken into account, but the visual representation should not be affected by this.

Actual Behavior

The text does not look good for some of the frames during window resize.

Basic Information

  • Version with issue: 2.88.3
  • Last known good version: unknown
  • IDE: Visual Studio
  • Platform Target Frameworks:
    • WinUI

Screenshots

67 68

Reproduction Link

SkiaScaleWinUI.zip

APopatanasov avatar Dec 15 '22 10:12 APopatanasov

Is it the same https://github.com/mono/SkiaSharp/issues/2295 ?

FrozDark avatar Dec 19 '22 06:12 FrozDark

It looks like to be the same.

APopatanasov avatar Dec 19 '22 08:12 APopatanasov

Is it possible that this bug is related to what I describe here in the GLFW forum?

https://discourse.glfw.org/t/canvas-jitter-flicker-when-resizing-with-multithreaded-render-loop/2188/11

I had assumed that the issue I describe there lies within GLFW or somewhere in that stack, although I suppose this could be due to an underlying issue with SkiaSharp.

bmitc avatar Dec 30 '22 01:12 bmitc

Specifically, here's the GIF uploaded to the GLFW support thread.

window-resize-issue

I don't have the ability to test this on macOS, as I don't have an Apple machine, and there is another SkiaSharp bug (https://github.com/mono/SkiaSharp/issues/2350) that is preventing me from drawing to an OpenGL context on Linux, so I can't test there either.

However, I have found out that, at least from what I currently can tell, this issue doesn't occur on every Windows machine. It occurs on two Windows 11 machines that have NVIDIA graphics cards, but from what I can see in my initial testing, I don't see it occur on a Windows 11 machine with only Intel Iris Xe. Of course, the issue is intermittent, so it can be shifty to determine if it's gone. Although it does normally show up somewhat readily.

bmitc avatar Jan 07 '23 14:01 bmitc

@APopatanasov @FrozDark Did either of you ever make any progress on working around this issue? It seems that what you describe is related to what I also encountered in the above video and linked GLFW discussion forum post, where resizing a window causes SkiaSharp to "jitter" even though I redraw as expected on every resize.

I was going to test @APopatanasov's suggested solutions, but I am unable to get SkiaSharp to build by following the instructions, so I'm not sure how to help.

bmitc avatar Aug 28 '23 03:08 bmitc

Does any OS let you synchronously couple window resize to window content drawing? I would think not. Because if that were the case, then any time rendering inside content is delayed, the OS-level window resize experience would become choppy. I highly assume OS devs make this impossible to achieve, without hacks (but I have not verified).

An app limiting OS window animation would be an avenue for DoS attacks.

What I've noticed in macOS is that no matter what the program inside a window is doing, window resize always works on its own with a separate framerate, decoupled from the rendering of the content inside the window. Not 100% sure about Windows except that I know this is true with all web browsers (maybe it was not true with IE at some point, IE was very easy to crash/exploit).

In the above example, I imagine that the rendering is slower than the framerate of the OS window resize, and that solving that is either impossible, or a hack that should not exist.

trusktr avatar Dec 17 '23 22:12 trusktr

Yes, you can synchronously block the resize with GLFW. That's what I used to debug this in the link I posted. I don't know if that blocks all other windows. I mean, once an app is running, it has a lot of ways to mess with the computer, so I don't get the DoS relevance.

The issue I described doesn't occur with GLFW and straight GL calls doing the drawing. It seems to be a SkiaSharp specific issue.

You reference macOS but what platform is that using? It's likely that it is a completely different stack (Cocoa window management with drawing done by Metal), so don't see the relevance. You can have smooth rendering occur during window resizes on Windows. It is just with OpenGL, you need to recreate the surface everytime the window is resized no matter what platform. There's no way around that. This SkiaSharp bug is preventing doing that reliably when using SkiaSharp as the drawing technology.

bmitc avatar Dec 20 '23 06:12 bmitc

@APopatanasov @FrozDark Did either of you ever figure this out? Or find a workaround?

bmitc avatar Apr 15 '24 20:04 bmitc

@APopatanasov @FrozDark Did either of you ever figure this out? Or find a workaround?

No. It's not necessary for me now.

FrozDark avatar Apr 16 '24 04:04 FrozDark

It would be neat if SKXamlCanvas.OnSizeChanged was virtual, just like SKXamlCanvas.OnPaintSurface. In that case it could be overridden and the suggested solution number 2 could be implemented externally :)

Mangepange avatar Apr 23 '24 14:04 Mangepange