openglopentkopenglcontext

Multiple IGraphicsContext with OpenTK / using multiple OpenGL contexts in a single window


What I want to do:

The main goal: Use SkiaSharp and OpenTK together. Render 2D and 3D.

What is the problem: SkiaSharp messes up the state of OpenGL, so I can't use it for 3D without saving and restoring some states.

Old solution (with OpenGL < 4): I used GL.PushClientAttrib(ClientAttribMask.ClientAllAttribBits); + some additional values (saved/restored them).

Now i read that this is not necessary the best solution and OpenGL 4 does not have GL.PushClientAttrib anymore. The usual way seems to be that you should use a seperate OpenGL context.

Have seen already: OpenTK multiple GLControl with a single Context

I am not using GLControl because I am not using WinForms. So this is not really helpful. What I tried:

internal class Program
{

    public static void Main(string[] args)
    {
        new Program().Run();
    }

    private readonly GameWindow _gameWindow;
    private IGraphicsContext _context2;

    private GlObject _glObject;
    private int _programId;

    private GlObject _glObject2;
    private int _programId2;

    public Program()
    {
        _gameWindow = new GameWindow(800,600,
            GraphicsMode.Default, "", GameWindowFlags.Default, 
            DisplayDevice.Default, 
            4, 2, GraphicsContextFlags.ForwardCompatible);

        _gameWindow.Resize += OnResize;
        _gameWindow.RenderFrame += OnRender;
        _gameWindow.Load += OnLoad;
    }

    public void Run()
    {
        _gameWindow.Run();
    }

    private void OnLoad(object sender, EventArgs e)
    {
        _programId = ShaderFactory.CreateShaderProgram();
        _glObject = new GlObject(new[]
        {
            new Vertex(new Vector4(-0.25f, 0.25f, 0.5f, 1f), Color4.Black),
            new Vertex(new Vector4(0.0f, -0.25f, 0.5f, 1f), Color4.Black),
            new Vertex(new Vector4(0.25f, 0.25f, 0.5f, 1f), Color4.Black),
        });

        _context2 = new GraphicsContext(GraphicsMode.Default, _gameWindow.WindowInfo, 4, 2,
            GraphicsContextFlags.Default);
        _context2.MakeCurrent(_gameWindow.WindowInfo);

        _programId2 = ShaderFactory.CreateShaderProgram();
        _glObject2 = new GlObject(new[]
        {
            new Vertex(new Vector4(-0.25f, 0.25f, 0.5f, 1f), Color4.Yellow),
            new Vertex(new Vector4(0.0f, -0.25f, 0.5f, 1f), Color4.Yellow),
            new Vertex(new Vector4(0.25f, 0.25f, 0.5f, 1f), Color4.Yellow),
        });

        _gameWindow.MakeCurrent();
    }

    private void OnRender(object sender, FrameEventArgs e)
    {
        _gameWindow.Context.MakeCurrent(_gameWindow.WindowInfo);
        GL.Viewport(0, 0, _gameWindow.Width, _gameWindow.Height);
        GL.ClearColor(0.3f,0.1f,0.1f,1);
        GL.Clear(ClearBufferMask.ColorBufferBit);

        GL.UseProgram(_programId);
        _glObject.Render();

        GL.Flush();
        _gameWindow.SwapBuffers();


        // i tried different combinations here
        // as i read GL.Clear will always clear the whole window
        _context2.MakeCurrent(_gameWindow.WindowInfo);
        GL.Viewport(10,10,100,100);
        //GL.ClearColor(0f, 0.8f, 0.1f, 1);
        //GL.Clear(ClearBufferMask.ColorBufferBit);

        GL.UseProgram(_programId2);
        _glObject2.Render();

        GL.Flush();
        _context2.SwapBuffers();
    }

    private void OnResize(object sender, EventArgs e)
    {
        var clientRect = _gameWindow.ClientRectangle;
        GL.Viewport(0, 0, clientRect.Width, clientRect.Height);
    }


}

Vertex shader:

#version 450 core

layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
out vec4 vs_color;

void main(void)
{
    gl_Position = position;
    vs_color = color;
}

Fragment shader:

#version 450 core
in vec4 vs_color;
out vec4 color;

void main(void)
{
    color = vs_color;
}

Works fine with a single context, when I use both contexts what happens is: first context gets rendered but flickers. There is no second triangle visible at all (as i understand GL.Viewport it should be visible on the lower left corner of the screen).

You could help me by answering one or more of the following questions:

  1. Is there another way to restore the original context
  2. Is there another way to render with HW acceleration on a part of the screen, ideally having specified OpenGL states for the specific area
  3. How can I get the solution above getting to work the way I want (no flicker but a smaller screen inside a smaller portion of the window)

Solution

  • After trying some more combinations what did the trick was:

    Call SwapBuffers only on the last used context in render (even when you use 3 contexts). Then no flicker will occur and rendering seems to work fine. State seems to be independent from each other.