Multiple Windows, same context, different results?

Started by piknikco, December 13, 2015, 22:28:18

Previous topic - Next topic

piknikco

Hello!

My engine is using LWJGL 3.0.0b with Java 8 on Linux. I have the following pseudo-code, which is in the order of how my engine initializes. Everything is running on the same "main" thread:

    public void sample() {
        //Do some required GLFW initialization
        if (glfwInit() != GL_TRUE) {
            throw new RuntimeException();
        }
        glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.err));

        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

        //Create an "init" window to create the context.
        glfwWindowHint(GLFW_VISIBLE, 0);
        glfwWindowHint(GLFW_DECORATED, 0);
        glfwWindowHint(GLFW_FLOATING, 0);
        glfwWindowHint(GLFW_RESIZABLE, 0);

        long initWindow = glfwCreateWindow(100, 100, "", NULL, NULL);
        glfwMakeContextCurrent(initWindow);

        //Create the OpenGL context.?
        GL.createCapabilities();

        //======================================
        //Renderer Initializes. Shaders compile, query attribute, uniform locations, etc.
        //======================================
        
        //Engine now creates another window which the main scene is drawn into.
        long mainWindow = glfwCreateWindow(800, 600, "My Engine Test", NULL, NULL);
        glfwMakeContextCurrent(mainWindow);
        glfwDestroyWindow(initWindow); //Initialization window is destroyed.
        
        //======================================
        //VBO batches uploaded
        //======================================
        
        //=================LOOP=================
        //Render the batch(es) onto the screen with glDrawArrays
        //=================LOOP=================
    }


The result is that none of my shaders are working. I am able to use glClearColor with varying colors to change the result, but nothing from my shaders draw.

However, if I instead skip the "initWindow" initialization and simply use the first window I create, everything works as expected. Why is that? Is the same OpenGL context I've created not being used in the other windows? I would assume that glfwMakeContextCurrent(window) would switch the current "main" thread context to the next window and keep all of the data.

The point of me doing it this way is to allow offscreen draws, and have multiple window and monitor management, so the process is quite necessary.

piknikco

I've solved the problem. All I've had to do was provide the pointer to the init window to the "share" parameter as so:

glfwCreateWindow(800, 600, "My Engine", NULL, initWindow);


And now the context is shared!

spasi

You need a GL.createCapabilities() call for every GLFW window you create (after making its context current with glfwMakeContextCurrent). GLFW creates a new OpenGL context for every window and technically you could get different OpenGL function pointers for each context. GL.createCapabilities() retrieves these function pointers, which are then used when you call OpenGL functions.

Why do you need the initWindow anyway?

piknikco

In my framework initialization process, the shaders are all read and compiled before the application can do anything. However, it can't do so without a context, so then comes the need for initWindow. I could probably architect it better but for now what I have will suffice.

Thanks for your help!

spasi

You could start with a hidden window and make it visible when shader compilation is complete, so you won't need a second window/context. Note that context sharing is usually associated with considerable overhead, even if you don't do heavyweight stuff like context switching. It's a good idea to avoid sharing, unless you really need it (e.g. loading resources/shaders in a secondary thread, while rendering at the same time).

piknikco

I don't understand something here if that's the case. I was thinking that there was only 1 context created per thread, which could be used for many windows, like in the case of a dual-monitor game. However, if there are multiple contexts, and as you say, context sharing is a big performance hit, then how is a dual-screen enabled game best implemented?

EDIT: Reading through the GLFW documentation taught me that a Window is inseparably linked from a graphics context, so I will have to live with context sharing, unfortunately...

spasi

Quote from: piknikco on December 14, 2015, 18:51:34I was thinking that there was only 1 context created per thread, which could be used for many windows

Technically you can, but GLFW doesn't expose it in its API (yet). You can also have multiple windows/contexts and a single thread. But only one context can be current at a given time.

Quote from: piknikco on December 14, 2015, 18:51:34if there are multiple contexts, and as you say, context sharing is a big performance hit, then how is a dual-screen enabled game best implemented?

I wouldn't say the performance hit is "big", but it's not trivial either. The driver is forced to handle race conditions when the same resource is accessed/updated by multiple contexts. My recommendation is, avoid it if you can.

Multi-screen gaming is best implemented with something like AMD Eyefinity or Nvidia Surround. That way you have a single window (and a single context) that spans multiple monitors.