Multi-window GL control

Hello, I’m currently making a C module for LUA and using SDL for the window control. its going great except the windows, currently they seem really unstable, slow, and unresponsive.

I want to control all SDL events from one thread, however when I try to - the window goes unresponsive. So I decided to just pollevents in each thread which you can assume how that went.

How do I properly poll events and control windows from one thread?

the source code in question is here: https://github.com/Elias-bff/Vanir/blob/main/src/modules/windows.c

At a glance, it looks like you’re only calling SDL_PollEvent() once per frame, which means that events will build up in the queue and won’t be handled until subsequent iterations of the main loop. That’s why there are nested loops in the example here, rather than a single ‘if’ statement in the main loop, as you have.

Yes, that solves the issue with performance - forgot to change it back to a while statement after testing something. the main issue still persists. it also broke closing and resizing

I haven’t dug too far into your source code, so I could be way off the mark, but I guess it looks concerning that the event loop is inside a window-specific function in what sounds like a multiple window environment. SDL_PollEvent() doesn’t know the context in which it’s called, and polls events for all windows. You can query the windowID member of SDL_WindowEvent to triage an event to the correct window, but the event loop itself and calls to SDL_PollEvent() should be window-independent. Is that helpful at all?

I tried making it it’s own thread, but the window turns unresponsive when no pollevent loop is running on it’s own thread :confused:

What do you mean by “unresponsive”? If you mean unresponsive to SDL events that you intend to handle yourself, then it would probably be helpful to see the code where you’re handling the events.

It is definitely not necessary to run an event loop for each window in a separate thread. E.g., here’s a single-threaded program that creates two windows, and shrinks whichever one you click on:

#include <stdio.h>
#include <stdbool.h>
#include "SDL.h"


#define DEFAULT_WINDOW_FLAGS SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL
#define DEFAULT_RENDER_FLAGS SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC

void init_SDL()
{
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        fprintf(stderr, "\nError initializing SDL: %s\n", SDL_GetError());
        exit(1);
    }
}

int main()
{
    init_SDL();
    SDL_Window *winA = SDL_CreateWindow("Window A", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 400, (Uint32)DEFAULT_WINDOW_FLAGS);
    SDL_Window *winB = SDL_CreateWindow("Window B", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 500, (Uint32)DEFAULT_WINDOW_FLAGS);
    SDL_Renderer *rendA = SDL_CreateRenderer(winA, -1, (Uint32)DEFAULT_RENDER_FLAGS);
    SDL_Renderer *rendB = SDL_CreateRenderer(winB, -1, (Uint32)DEFAULT_RENDER_FLAGS);

    bool quit = false;
    while (!quit) {
        SDL_Event e;
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT || (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE)) {
                quit = true;
            } else if (e.type == SDL_MOUSEBUTTONDOWN) {
                int w, h;
                if (e.button.windowID == SDL_GetWindowID(winA)) {
                    SDL_GetWindowSize(winA, &w, &h);
                    SDL_SetWindowSize(winA, w - 10, h - 10);
                    fprintf(stdout, "Handle mouse click for Window A\n");
                } else if (e.button.windowID == SDL_GetWindowID(winB)) {
                    SDL_GetWindowSize(winB, &w, &h);
                    SDL_SetWindowSize(winB, w - 10, h - 10);
                    fprintf(stdout, "Handle mouse click for Window B\n");
                }
            }
        }
        SDL_SetRenderDrawColor(rendA, 0, 0, 0, 0);
        SDL_SetRenderDrawColor(rendB, 0, 0, 0, 0);

        SDL_RenderClear(rendA);
        SDL_RenderClear(rendB);

        SDL_RenderPresent(rendA);
        SDL_RenderPresent(rendB);
    
        SDL_Delay(1);
    }
}

I would even go so far as to say that it’s wrong to have more than one SDL_PollEvent loop because they would steal events from each other.

Also note that the wiki says

As this function may implicitly call SDL_PumpEvents(), you can only call this function in the thread that set the video mode.

so it sounds like it’s not designed to be used from different threads at all.

1 Like

i know, i tried that - but when I move the sdl polling outside into its own thread - with only ONE poll event function. it just makes all windows go unreponsive. it does that unless i have poll event running in them

with what im doing i have to have it in its own thread so the main thread can return 0

Some operating systems require anything that interacts with the UI (creating windows, responding to UI events, etc.) get handled on the main thread or else you get undefined behavior. IIRC, macOS and iOS only deliver events to the main queue/thread.

So you should init SDL, create your windows, and have your event loop on the main thread.

You can then do the OpenGL rendering on another thread if you want to.

the main thread has to return 0 so the lua can continue on the main thread. if i could poll events on a dedicated thread or move the entire sdl to a new thread (?), that would be perfect

What do you mean “the main thread has to return 0 so the lua can continue on the main thread.” ?

When people are talking about the “main” thread, they mean the one your program starts on. When the main thread returns 0, your program exits and that’s it.

ah, sorry i was thinking of the main entrance to my module - i forgot theres a actual main function, one moment

Oh, you’re talking about the Lua interpreter calling your function. There’s no way around having an event loop in the main thread.

There are a few options, as I see it:

  1. Your module exposes a function that the Lua program will need to call frequently, and that function processes any pending events
  2. Your module wraps the SDL event loop and exposes it to the Lua program to deal with
  3. Instead of a Lua module, make a standalone program that embeds the Lua interpreter. Maybe have the Lua interpreter run in a separate thread (though you’ll have to make sure anything that touches the UI etc gets done on the main thread). Or you could just run the interpreter a few instructions at a time in your main loop.
  4. Use Lua bindings for an actual UI toolkit, like wxLua or QT

mhm, i was trying every way possible to try and work in a main thread way of using pollevent but it wouldnt budge :confused:

i was just thinking about just exposing a function to lua like you said. similarily i run all hooks created in c in that kind of way. ill just attach sdl processing to that as well

while embedding would be the easy way out its definitely not the route i want to take. i might switch to using a ui toolkit maybe - depends