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] Drawing is warped during window resize on NVIDIA graphics card #2832
I have a project where I am using SkiaSharp to draw to a window provided by GLFW. In the code below, I have reproduced the issue with just Silk.NET's GLFW bindings (whereas I am normally using my own bindings) and SkiaSharp. The code is not doing anything fancy. It simply draws a circle while the window is open, and during a resize, the entire surface is recreated and redrawn.
The issue is that when the window is resized, the drawing is stretched or compressed during the resize. It isn't clear what is causing this, as the entire surface is recreated during the resize callback and the drawing redrawn, and the callback is blocking. In this case, only a constant sized circle in a constant location is drawn, so it should be impossible for the drawing to contain anything other than a circle. However, the circle is often stretched into an ellipse in either or both directions during a window resize.
What's curious is that this does not occur when integrated Intel graphics are used. It only seems to occur when NVIDIA graphics are used. Thus, it is unclear whether this is a SkiaSharp problem, a Skia problem, whatever backend graphics API is being chosen by Skia or SkiaSharp, or an NVIDIA driver problem. I have searched, and it seems pathological resize behavior has occurred in various forms in all three contexts.
This issue prevents any proper resize functionality in a window.
Code
Attached is a .zip of the source file and project. Below is a copy of the source file. This issue occurs on any version of SkiaSharp I have tried, including the latest stable 2.88.8.
open FSharp.NativeInterop
open Silk.NET.GLFW
open SkiaSharp
#nowarn "9"
let initialWidth, initialHeight = 500, 500
let mutable framebufferWidth, framebufferHeight = initialWidth, initialHeight
let glfw = Glfw.GetApi()
glfw.Init() |> printfn "Initialized?: %A"
// Uncomment these window hints if on macOS
//glfw.WindowHint(WindowHintInt.ContextVersionMajor, 3)
//glfw.WindowHint(WindowHintInt.ContextVersionMinor, 3)
//glfw.WindowHint(WindowHintBool.OpenGLForwardCompat, true)
//glfw.WindowHint(WindowHintOpenGlProfile.OpenGlProfile, OpenGlProfile.Core)
glfw.WindowHint(WindowHintInt.Samples, 0)
glfw.WindowHint(WindowHintInt.StencilBits, 1)
glfw.WindowHint(WindowHintBool.DoubleBuffer, true)
glfw.WindowHint(WindowHintBool.Focused, false)
glfw.WindowHint(WindowHintBool.Maximized, false)
glfw.WindowHint(WindowHintBool.Visible, true)
let window = glfw.CreateWindow(initialWidth, initialHeight, "Test Window", NativePtr.ofNativeInt 0n, NativePtr.ofNativeInt 0n)
printfn "Window: %A" window
glfw.MakeContextCurrent(window)
let mutable error = nativeint<byte> 1uy |> NativePtr.ofNativeInt
glfw.GetError(&error) |> printfn "Error: %A"
let grGlInterface = GRGlInterface.Create(fun name -> glfw.GetProcAddress name)
if not (grGlInterface.Validate()) then
raise (System.Exception("Invalid GRGlInterface"))
let grContext = GRContext.CreateGl(grGlInterface)
let grGlFramebufferInfo = new GRGlFramebufferInfo(0u, SKColorType.Rgba8888.ToGlSizedFormat()) // 0x8058
let draw(window, width, height) =
glfw.PollEvents()
grContext.ResetContext()
let grBackendRenderTarget = new GRBackendRenderTarget(width, height, 1, 0, grGlFramebufferInfo)
let surface = SKSurface.Create(grContext, grBackendRenderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888)
let canvas = surface.Canvas
canvas.Clear(SKColors.LightBlue)
let red = new SKPaint(Color = SKColors.Black)
canvas.DrawCircle(float32(300)/2.0f, float32(300)/2.0f, 100.0f, red)
canvas.Flush()
glfw.SwapBuffers(window)
red.Dispose()
surface.Dispose()
grBackendRenderTarget.Dispose()
let resizeCallbackFun window width height = draw(window, width, height)
glfw.SetFramebufferSizeCallback(window, resizeCallbackFun) |> ignore
while glfw.WindowShouldClose window <> true do
glfw.GetFramebufferSize(window, &framebufferWidth, &framebufferHeight)
draw(window, framebufferWidth, framebufferHeight)
glfw.DestroyWindow window
glfw.Terminate()
Expected Behavior
I expected that the resize happens as it does in the integrated Intel graphics situation, where the image drawn inside the main loop and resize callback is always as specified and not warped.
I am running Windows 11. I currently can't run SkiaSharp on Linux, and I don't have a macOS machine to test on.
winver returns Version 23H2 (OS Build 22631.3447).
Devices
Dell XPS Desktop
Dell XPS 15 laptop
Dell XPS 17 laptop
Relevant Screenshots
You can see in this screenshots how the circle is stretched. The issue seems to be related to a race condition somewhere. In the underlying stack, as the only time a circle is drawn in the top-level code is inside the draw function, and it's a constant circle in the same location every time, so the stretching is being performed by something else.
Description
I have a project where I am using SkiaSharp to draw to a window provided by GLFW. In the code below, I have reproduced the issue with just Silk.NET's GLFW bindings (whereas I am normally using my own bindings) and SkiaSharp. The code is not doing anything fancy. It simply draws a circle while the window is open, and during a resize, the entire surface is recreated and redrawn.
The issue is that when the window is resized, the drawing is stretched or compressed during the resize. It isn't clear what is causing this, as the entire surface is recreated during the resize callback and the drawing redrawn, and the callback is blocking. In this case, only a constant sized circle in a constant location is drawn, so it should be impossible for the drawing to contain anything other than a circle. However, the circle is often stretched into an ellipse in either or both directions during a window resize.
What's curious is that this does not occur when integrated Intel graphics are used. It only seems to occur when NVIDIA graphics are used. Thus, it is unclear whether this is a SkiaSharp problem, a Skia problem, whatever backend graphics API is being chosen by Skia or SkiaSharp, or an NVIDIA driver problem. I have searched, and it seems pathological resize behavior has occurred in various forms in all three contexts.
This issue prevents any proper resize functionality in a window.
Code
Attached is a .zip of the source file and project. Below is a copy of the source file. This issue occurs on any version of SkiaSharp I have tried, including the latest stable 2.88.8.
SilkNETGLFWWindowTest.zip
Expected Behavior
I expected that the resize happens as it does in the integrated Intel graphics situation, where the image drawn inside the main loop and resize callback is always as specified and not warped.
Actual Behavior
Resizing with integrated Intel graphics
https://github.com/mono/SkiaSharp/assets/65685447/10333812-40fc-4aed-8910-f31afe935d0f
Resizing with NVIDIA graphics
https://github.com/mono/SkiaSharp/assets/65685447/bf94c867-fba3-4669-a6f8-f59405e9e69f
Version of SkiaSharp
3.x (Alpha)
Last Known Good Version of SkiaSharp
Other (Please indicate in the description)
IDE / Editor
Visual Studio (Windows)
Platform / Operating System
Windows
Platform / Operating System Version
I am running Windows 11. I currently can't run SkiaSharp on Linux, and I don't have a macOS machine to test on.
winver
returnsVersion 23H2 (OS Build 22631.3447)
.Devices
Relevant Screenshots
You can see in this screenshots how the circle is stretched. The issue seems to be related to a race condition somewhere. In the underlying stack, as the only time a circle is drawn in the top-level code is inside the
draw
function, and it's a constant circle in the same location every time, so the stretching is being performed by something else.Relevant Log Output
No response
Code of Conduct