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.