Issues with drawing to Skia canvas with GLFW provided GL context

368 views
Skip to first unread message

bmitc

unread,
Jun 25, 2022, 4:59:49 PM6/25/22
to skia-discuss
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:
  1. 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.
  2. What is the correct ordering of glfwPollEvents, grContext.ResetContext, canvas.Flush, glfwSwapBuffers, and my canvas drawing functions?
  3. What is the difference between canvas.Flush() and surface.Flush()? Is only one required or are both required?
  4. 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).
  5. 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.
  6. 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!

bmitc

unread,
Jun 25, 2022, 5:01:34 PM6/25/22
to skia-discuss
Also, here is a link to a GitHub issue that I have filed:  [BUG] `System.AccessViolationException` thrown when flushing canvas or swapping buffers · Issue #2125 · mono/SkiaSharp (github.com)

It contains better formatting for the F# code.

Jim Van Verth

unread,
Jun 27, 2022, 9:33:44 AM6/27/22
to skia-d...@googlegroups.com
Is it possible that the bound framebuffer is not at ID = 0? You can confirm the value by using glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo_id);

--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/skia-discuss/f03897eb-d9de-4f93-a5bd-1a8b5f003281n%40googlegroups.com.


--

Jim Van Verth | Software Engineer | jvan...@google.com | 919-210-7664

bmitc

unread,
Jun 27, 2022, 4:22:02 PM6/27/22
to skia-discuss
Hi Jim,

Thank you for the response! I am not sure I have access to that exact function through SkiaSharp.

I am creating a GRGlFramebufferInfo struct using the constructor that specifies the framebuffer object ID. I went and printed out the GRGlFramebufferInfo.FramebufferObjectId after creation, and it was zero. Are you thinking that it is changing at some point during the draw cycle? Note that I recreate the surface every draw cycle since the width and height may change. I am not really sure what the framebuffer is other than this abstract buffer thing that gets drawn to or what the FBO ID determines. Any elucidation there would be appreciated.

By the way, this same code (with the OpenGL window hints set appropriately to OpenGL 4.1) crashes the `dotnet` process on macOS. I'm assuming it's something similar going on there. I'm really not for sure. I've compared my code to others, and it doesn't appear I'm doing anything wrong.
Reply all
Reply to author
Forward
0 new messages