SFML community forums

General => General discussions => Topic started by: papush! on February 03, 2018, 09:53:54 pm

Title: Window.waitEvent() and multithreaded input handling.
Post by: papush! on February 03, 2018, 09:53:54 pm
Hi,
the official documentation for Window.waitEvent() says its main use is when called from a dedicated input handling thread, but after some experimentation and reading around it seems the only way to handle input in a separate thread is to:

So I guess I’m left with three questions:

I’m just making a simple top-down 2D game, so I’m probably overthinking a lot of stuff, but it got me curious and I’d appreciate if someone could shine some light on the matter.
Title: Re: Window.waitEvent() and multithreaded input handling.
Post by: Laurent on February 04, 2018, 11:14:52 am
All your questions seem to be related to this conclusion:

Quote
you can’t draw on the window while waiting for an event

What makes you think it is impossible? It should not. Please describe your problem precisely, instead of jumping to conclusions directly ;)
Title: Re: Window.waitEvent() and multithreaded input handling.
Post by: papush! on February 04, 2018, 12:10:59 pm
Well attempting to do so either results in a bunch of “Failed to activate the window's context” followed by a “XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server: 0”, or an immediate “XInitThreads has not been called”. Considering that other people were having the same problem (and calling XInitThread isn’t cross-platform), I thought it was just impossible.

Here is a basic example that produces either of those errors (I’m guessing depending on wether draw() or waitEvent() gets called first):
#include <thread>
#include <memory>
#include <SFML/Graphics.hpp>

sf::Sprite sprite;
bool quit = false;

void draw(sf::RenderWindow *window) {
    while (!quit) {
        window->draw(sprite);
        window->display();
    }
}

int main() {
    sf::Texture texture;
    texture.loadFromFile("poivron.png");
    sprite = sf::Sprite(texture);
    sf::RenderWindow window(sf::VideoMode(640, 360), "test");
    window.setFramerateLimit(300);

    std::thread rendering(draw, &window);

    sf::Event event;
    while (true) {
        if (window.waitEvent(event)) {
            if (event.type == sf::Event::Closed)
                break;
        }
        else
            break;
    }
    quit = true;
    rendering.join();
}
 

Am I doing something wrong? I guess I should have made a post in the help forum first, sorry about that.

Edit, to clarify my point:
Synchronising the threads to prevent concurrent access like this:
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>
#include <memory>
#include <SFML/Graphics.hpp>

std::mutex m;
sf::Sprite sprite;
bool quit = false;

void draw(sf::RenderWindow *window) {
    unsigned i = 0;
    while(!quit) {
        sprite.setPosition((i % 10) * 30, (i / 10) * 30);
        i++;
        if (i > 100) {
            i = 0;
        }
        m.lock();
        std::cout << "Rendering." << std::endl;
        window->clear();
        window->draw(sprite);
        window->display();
        m.unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    sf::Texture texture;
    texture.loadFromFile("poivron.png");
    sprite = sf::Sprite(texture);
    sf::RenderWindow window(sf::VideoMode(640, 360), "test");
    window.setFramerateLimit(300);

    std::thread rendering(draw, &window);
    window.setActive(false);

    sf::Event event;
    while (true) {
        m.lock();
        std::cout << "Waiting for an event…" << std::endl;
        bool ok = window.waitEvent(event);
        std::cout << "Event received." << std::endl;
        m.unlock();
        if (ok) {
            if (event.type == sf::Event::Closed)
                break;
        }
        else
            break;
    }
    quit = true;
    rendering.join();
}
 
effectively gets rid of the errors and crashes, but then the rendering is halted while waiting for an event (see attachment).
Title: Re: Window.waitEvent() and multithreaded input handling.
Post by: eXpl0it3r on February 04, 2018, 01:31:07 pm
You need to activate a context in the thread you want to use OpenGL (through SFML).

window.setActive(true)
Title: Re: Window.waitEvent() and multithreaded input handling.
Post by: Laurent on February 04, 2018, 07:52:31 pm
You're facing two well-known limitations of the lower levels (OS and OpenGL), which are expected and described in the documentation (not sure for XInitThreads, but the information can easily be found on the forum):

1. On Linux, with XLib, you need to call XInitThreads() when you're managing your window from multiple threads; it's not multi-platform but it is needed anyway, so just use some preprocessor conditions to make that call only on Linux.

2. As eXpl0it3r said, if drawing happens in a different thread, you need to deactivate the window from the thread where it is active, and activate it in the thread that makes the draw calls.
Title: Re: Window.waitEvent() and multithreaded input handling.
Post by: papush! on February 04, 2018, 09:03:51 pm
That cleared it up, thanks, you guys are doing great work  :)