Hello. I am writing my own bindings to GLFW with F# and using SkiaSharp to draw to a GLFW window's context. The goal of this project is to have cross-platform, desktop only GUI system where GLFW provides the window and events and Skia does all the drawing. This is so that I stay away from OpenGL as much as possible, setup a Window class for a GLFW window that handles all drawing loops, events, and callbacks, and then I use SkiaSharp to draw GUI elements.
Due to lack of documentation and a dearth of examples across the board, I am really struggling with understanding what the correct recipe is for this interaction or where to go to correct misunderstanding. So, this is a plea for help! I have spent hours and days on this, and I'm just not sure where to go from here without some help.
Right now, my biggest problem is that Skia or SkiaSharp crashes in my draw loop. The two errors I get printed to the console are:
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
at SkiaSharp.SkiaApi.sk_refcnt_safe_unref(IntPtr)
--------------------------------
at SkiaSharp.SKObjectExtensions.SafeUnRef(SkiaSharp.ISKReferenceCounted)
at SkiaSharp.SKNativeObject.Dispose(Boolean)
at SkiaSharp.SKNativeObject.Finalize()
and
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
at SkiaSharp.SkiaApi.sk_canvas_flush(IntPtr)
--------------------------------
at SkiaSharp.SKCanvas.Flush()
at Windowing.Window+Window.Draw()
at <StartupCode$WindowTest>.$Program.main@()
(This latter error occurs in a program with some higher-level wrappers of the program posted below. The calls to GLFW and SkiaSharp are essentially identical. I don't know why I separate error is reported.)
The program I am using as a test fixture is below. It is written in F# with my own bindings and wrappers for GLFW and using SkiaSharp, however, the naming should basically map directly to GLFW and Skia.
let mutable width, height = 500, 500
let mutable framebufferWidth, framebufferHeight = width, height
let init = glfwInit() |> printfn "Initialized?: %A"
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3)
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
let window = glfwCreateWindow(width, height, "Test Window", NULL_MONITOR, NULL_WINDOW)
glfwMakeContextCurrent(window)
let grGlInterface = GRGlInterface.Create(getProcAddress)
if not (grGlInterface.Validate())
then raise (System.Exception("Invalid GRGlInterface"))
let grContext = GRContext.CreateGl(grGlInterface)
let draw(window, width, height) =
glfwPollEvents()
grContext.ResetContext()
let grGlFramebufferInfo = new GRGlFramebufferInfo(0u, uint32(GL_RGBA8))
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.Cyan)
let red = new SKPaint(Color = SKColor(byte(255), byte(0), byte(0), byte(255)))
canvas.DrawCircle(float32(width)/2.0f, float32(height)/2.0f, float32(100), red)
canvas.Flush()
glfwSwapBuffers(window)
while glfwWindowShouldClose window <> 1 do
glfwGetFramebufferSize(window, &framebufferWidth, &framebufferHeight)
draw(window, framebufferWidth, framebufferHeight)
glfwDestroyWindow(window)
terminate()
While I am indeed using my own F# bindings to GLFW, which are .NET bindings for Skia, I'm fairly certain the issue lies within SkiaSharp, Skia, or how I'm calling SkiaSharp. The GLFW bindings are straightforward. Currently, these errors are occurring in Windows 11. macOS is a whole other ballgame, where the window usually crashes immediately with a different issue, so I'll have to get to that later at some point.
My questions are:
- How do I get rid of this crash that seems to happen around canvas.Flush()? The crash happens every time, but it does not happen immediately. Until the crash happens, the window draws and behaves as expected. The time it takes to crash is indeterminate. If I remove the drawing of the red circle and just have the canvas.Clear(...), there is no crash.
- What is the correct ordering of glfwPollEvents, grContext.ResetContext, canvas.Flush, glfwSwapBuffers, and my canvas drawing functions?
- What is the difference between canvas.Flush() and surface.Flush()? Is only one required or are both required?
- It is unclear to me what I should use for the sampleCount and stencilBits when creating the GRBackendRenderTarget. Do I need to use window hints to additionally set this with GLFW? The various examples I've seen use different values for sampleCount (usually 0, 1, or 8) and stencilBits (usually 0).
- I'm not sure of the correct values for the GLFW_CONTEXT_VERSION_MAJOR, GLFW_CONTEXT_VERSION_MINOR, GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_PROFILE across OSs that are supposed to work well with Skia.
- Where do I go to learn more about this stuff and improve my understanding? The only system that has any documentation around this is GLFW. I have only found random examples of Skia and a GL context. I would like to reach a point of knowledge where I know exactly what's required for this GLFW and Skia interaction, as I want the windowing and drawing to be as bug free as possible.
Thank you for any help! It is much needed!