Full frame-rate across multiple OpenGL windows?

Viewed 267

I'm trying to make an OpenGL program with as many windows as I want. I know it's possible. I don't care if I have to use something other than SDL2, but I've tried GLFW3 and get the same results.

The problem is not that I can't create multiple windows or can't render to both of them. I can. The problem is no matter how I try to do it, whether I use a single thread or multiple, the frame rate is locked to a divisor of my monitor's refresh rate. What I mean is, if I have two windows rendering on the main thread and updated using SDL_GL_SwapWindow(window) I get 72fps for both of them (My monitor has a refresh rate of 144hz and 144/2=72hz). This is expected for a single thread because of v-sync. Each call to SDL_GL_SwapWindow is syncing the window to the refresh rate and 1/144 + 1/144 is 1/72. That's not the problem. The problem is I have tried using separate threads for each window and even then it runs at 72fps even though they should be rendering/swapping the buffers at the same time.

Example 1:

In this example, I have both windows rendering and swapping buffers on the main thread. This does what you would expect on a monitor with a refresh rate of 144hz. They render at 72fps.

#include <iostream>

#include <SDL2/SDL.h>
#undef main

#include <glad/glad.h>

int main() {
    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window* w1 = SDL_CreateWindow("w1", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL);
    SDL_Window* w2 = SDL_CreateWindow("w2", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 700, SDL_WINDOW_OPENGL);

    SDL_GLContext c1 = SDL_GL_CreateContext(w1);
    SDL_GLContext c2 = SDL_GL_CreateContext(w2);

    SDL_GL_MakeCurrent(w1, c1);
    gladLoadGL();

    int r = 0, b = 0;

    bool running = true;
    while (running) {
        uint64_t startTime = SDL_GetPerformanceCounter();

        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = false;
            } else if (event.type == SDL_WINDOWEVENT) {
                if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
                    SDL_GL_DeleteContext(c2);
                    SDL_DestroyWindow(w2);
                }
            }
        }

        SDL_GL_MakeCurrent(w1, c1);
        glClearColor(r / 255.0f, 0, 0, 1);
        r = (r + 1) % 256;
        glClear(GL_COLOR_BUFFER_BIT);
        SDL_GL_SwapWindow(w1);

        SDL_GL_MakeCurrent(w2, c2);
        glClearColor(0, 0, b / 255.0f, 1);
        b = (b + 1) % 256;
        glClear(GL_COLOR_BUFFER_BIT);
        SDL_GL_SwapWindow(w2);

        double deltaTime = (SDL_GetPerformanceCounter() - startTime) / (double)SDL_GetPerformanceFrequency();
        std::cout << 1.0 / deltaTime << " fps" << std::endl;
    }

    SDL_GL_DeleteContext(c1);
    SDL_DestroyWindow(w1);

    SDL_Quit();
    return 0;
}

Example 2:

In this example, I have both windows rendering and swapping buffers on their own threads. This doesn't do what I expect because it behaves the exact same as having them both on the same thread like in example 1.

#include <iostream>
#include <thread>

#include <SDL2/SDL.h>
#undef main

#include <glad/glad.h>

void RenderThread(bool& running, SDL_Window* window) {
    SDL_GLContext context = SDL_GL_CreateContext(window);
    SDL_GL_MakeCurrent(window, context);
    gladLoadGL();

    int r = 0;
    while (running) {
        uint64_t startTime = SDL_GetPerformanceCounter();

        glClearColor(r / 255.0f, 0, 0, 1);
        r = (r + 1) % 256;
        glClear(GL_COLOR_BUFFER_BIT);
        SDL_GL_SwapWindow(window);

        double deltaTime = (SDL_GetPerformanceCounter() - startTime) / (double)SDL_GetPerformanceFrequency();
        std::cout << 1.0 / deltaTime << " fps" << std::endl;
    }

    SDL_GL_DeleteContext(context);
}

int main() {
    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window* w1 = SDL_CreateWindow("w1", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL);
    SDL_Window* w2 = SDL_CreateWindow("w2", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 700, SDL_WINDOW_OPENGL);

    bool running = true, r1 = true, r2 = true;
    std::thread t1(RenderThread, std::ref(r1), w1);
    std::thread t2(RenderThread, std::ref(r2), w2);

    while (running) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = false;
                r1 = false;
                t1.join();
            } else if (event.type == SDL_WINDOWEVENT) {
                if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
                    r2 = false;
                    t2.join();
                    SDL_DestroyWindow(w2);
                }
            }
        }
    }

    SDL_DestroyWindow(w1);

    SDL_Quit();
    return 0;
}

I'm fine with using another library for the windows. If my problem can be fixed with a different library please tell me which one.

0 Answers
Related