Using SkiaSharp with GLFW

TounToun FRMember

Hello,

I'm trying to get a fully portable Net Core app using SkiaSharp. To get cross platform windows I decided to go for GLFW for which I made very basic bindings. The bindings are just enough to create a window and get a OpenGL context handle. They seem to be working well.

Now, I'm trying to get SkiaSharp to use the already existing OpenGL context and default framebuffer. The following code runs without error but Skia doesn't seem to actually draw anything in the buffer.

GLFW.Init();
GLFW.SetErrorCallback((error, description) => Console.WriteLine("Error " + error + ": " + description));
GLFW.WindowHint(Hint.Visible, 1);
GLFW.WindowHint(Hint.Samples, 0);
GLFW.WindowHint(Hint.Resizable, 0);
Window window = GLFW.CreateWindow(300, 300, "Hello World!");
GLFW.MakeContextCurrent(window);

GRGlInterface glInterface = GRGlInterface.AssembleGlInterface(GLFW.GetWGLContext(window), (contextHandle, name) => GLFW.GetProcAddress(name));
GRContext context = GRContext.Create(GRBackend.OpenGL, glInterface);
GRBackendRenderTargetDesc backendRenderTargetDescription = new GRBackendRenderTargetDesc
{
    Config = GRPixelConfig.Rgba8888,
    Height = 300,
    Width = 300,
    Origin = GRSurfaceOrigin.TopLeft,
    RenderTargetHandle = new IntPtr(0),
    SampleCount = 0,
    StencilBits = 8
};

using (var surface = SKSurface.Create(context, backendRenderTargetDescription)) {
    var canvas = surface.Canvas;

    while (!GLFW.WindowShouldClose(window))
    {
        GLFW.PollEvents();
        canvas.Clear(SKColors.White);
        GLFW.SwapBuffers(window);
    }
}

GLFW.DestroyWindow(window);
GLFW.Terminate();

The full code can be found here: https://gist.github.com/tristandaniel/15d8d3eac365ba3179d105247ed9f961

Also I found a C example doing this but I didn't manage to match the functions exactly with SkiaSharp: https://gist.github.com/zester/5163313

Any idea?

And sorry for using Xamarin forums for non-xamarin-related problems :x

Thanks!

Best Answer

Answers

  • TounToun FRMember
    edited July 2017

    Well, first suggestion was spot on!

    I just added canvas.Flush() after canvas.Clear(SKColors.White);, and bingo. Not sure why tho, is Skia drawing asynchronously (which can only be good I guess)? In pure OpenGL I usually don't have to flush the pipeline. context.Flush() also does the trick, I dont really know either which is better.

    And as for the render target handle, 0 is always the default framebuffer in OpenGL. But just to make sure it works on all implementations, your suggestion probably is wiser.

    Just another question: when resizing my window, the render target is automatically resized. And surprisingly, Skia seems to also detect the change and adapt its internal render target. Is this a reliable behaviour or should I somehow tell Skia that the render target has been resized?

    Thank you so much for the precious help, my dream of clean crossplatform C# UI might be getting real :)

    Edit: Okay my bad, in fact Skia doesnt seem to resize its internal render target description. It's weird because it still clears the whole framebuffer, but won't draw on it. That's probably because Skia calls glClear which does not need a size parameter.

  • mattleibowmattleibow ZAXamarin Team Xamurai

    Awesome!

    I think the canvas.Flush() may be triggering the context.Flush()... The context sometimes batches draw operations on some platforms, so this may be why.

    About the size... I think it may be best to update the size - you may be getting lucky with only a single view. If you draw over, the GL just accepts it. If you change the dimensions in some cases, there may be artifacts... Also, there may be the case that it is just stretching the drawing to fit. Best practice would be to set the size before drawing. This was you can set the size when the view changes, and then later just use the desc to read the size.

  • TounToun FRMember
    edited July 2017

    Okay, so basically when the framebuffer gets resized I update the GRBackendRenderTargetDesc and then call SKSurface.Create again. Could calling SKSurface.Create every frame be a performance issue? Or is it really just a C# object that gets created, and no heavy GL operation is done in the background?

    Edit: From what I understand of the code (https://github.com/google/skia/blob/78f1f5a5e74692900e9e23d7f7026a9ca03498ac/src/image/SkSurface_Gpu.cpp#L246) some stuff is still going on in Skia so I will at least make the optimisation of not calling it if the framebuffer hasn't changed.

    Anyway thanks again! I guess this is now resolved.

  • mattleibowmattleibow ZAXamarin Team Xamurai

    I am not too sure on that point... You may want to ask the guys at Google: https://groups.google.com/forum/#!forum/skia-discuss

    And, I think the real work is done in the GL interface and context, but I may be wrong. I have not noticed that much of an issue in my views (but I have not measured) where I re-create the surface for each frame. I reuse the interface and context, but not the surface.

Sign In or Register to comment.