Salut, j'ai testé un code pour faire l'affichage et gestion d'évènement dans un thread, et le rendu dans un autre thread, avec les thread du c++11, mais celà ne marche pas, voici le code :
Thread d'affichage :
#ifndef DISPLAYER
#define DISPLAYER
#include "updater.h"
#include <iostream>
class RenderComponent {
public :
RenderComponent();
void start();
void run();
void setActive(bool b);
private :
sf::RenderWindow window;
std::thread m_thread;
Updater updater;
bool running;
};
#endif // DISPLAYER
#include "displayer.h"
using namespace std;
using namespace sf;
RenderComponent::RenderComponent () : updater(*this), window (sf::VideoMode(800, 600), "My window") {
running = false;
}
void RenderComponent::setActive(bool b) {
window.setActive(b);
}
void RenderComponent::start() {
updater.start();
running = true;
m_thread = thread (&RenderComponent::run, this);
}
void RenderComponent::run() {
// création de la fenêtre
while (running) {
// on fait tourner le programme tant que la fenêtre n'a pas été fermée
while (window.isOpen())
{
// on traite tous les évènements de la fenêtre qui ont été générés depuis la dernière itération de la boucle
sf::Event event;
while (window.pollEvent(event))
{
// fermeture de la fenêtre lorsque l'utilisateur le souhaite
if (event.type == sf::Event::Closed) {
window.close();
running = false;
m_thread.join();
}
}
// effacement de la fenêtre en noir
/*unique_lock<mutex> locker(g_lock_update_component);
g_signal.wait(locker, [&](){return !updater.isGDone();});*/
window.clear(sf::Color::Black);
window.draw(updater.getRectangle());
window.display();
}
}
}
Thread de rendu :
#ifndef UPDATER
#define UPDATER
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <thread>
#include "globals.h"
#include <random>
class RenderComponent;
class Updater {
public :
Updater(RenderComponent &renderComponent);
void start();
void stop ();
void run();
bool isGDone();
sf::RectangleShape getRectangle ();
private :
std::thread m_thread;
sf::RectangleShape rectangle;
bool running, gDone;
RenderComponent &renderComponent;
};
#endif // UPDATER
[code=cpp]
#include "updater.h"
#include "displayer.h"
using namespace std;
using namespace sf;
Updater::Updater(RenderComponent &renderComponent) : renderComponent (renderComponent) {
rectangle = RectangleShape(Vector2f(0, 0));
rectangle.setSize(Vector2f(100, 100));
running = gDone = false;
}
bool Updater::isGDone() {
return gDone;
}
void Updater::start() {
running = true;
m_thread = thread (&Updater::run, this);
}
void Updater::stop() {
running = false;
m_thread.join();
}
void Updater::run() {
while (running) {
/*renderComponent.setActive(false);
unique_lock<mutex> locker(g_lock_update_component);*/
renderComponent.setActive(false);
gDone=false;
int posX = rand() % 800;
int posY = rand() % 600;
rectangle.setPosition(posX, posY);
gDone = true;
renderComponent.setActive(true);
// g_signal.notify_one();
}
}
RectangleShape Updater::getRectangle () {
return rectangle;
}
Et le main :
#include "displayer.h"
#include <random>
#include <ctime>
int main () {
srand(time(NULL));
RenderComponent renderComponent;
renderComponent.start();
return EXIT_SUCCESS;
}
Il m'affiche le message suivant en console : Failed to activate the window's context.
La SFML est mal foutue à tout les coups , ou encore un bug de l'os...
Tu n'as peu être pas écrit assez gros dans les tutoriels...
Allez , je suis sympa , je t’évite quelques clics :
Les évènements doivent être traités dans le thread de la fenêtre
C'est une limitation importante de la plupart des OS : la boucle d'évènements (plus précisément, la fonction pollEvent ou waitEvent) doit être appelée dans le thread qui a créé la fenêtre. Cela implique que si vous voulez créer un thread dédié à la gestion d'évènements, vous devrez vous assurer que la fenêtre est créée dans ce même thread. Si vous voulez vraiment séparer les choses en plusieurs threads, il est plutôt recommendé de garder le fenêtrage et les évènements dans le thread principal, et de bouger le reste (rendu, physique, logique, ...) dans des threads. Cette configuration a aussi l'avantage d'être compatible avec la limitation qui suit.
Bon je ne sais pas pourquoi mais en fait, quand on fait l'affichage autre part que dans le thread main, ça ne marche pas, ça, ça marche mieux : (J'en ai profité pour passer par Qt pour faire l'affichage dans un thread.)
#include "mycanvas.h"
using namespace sf;
using namespace std;
MyCanvas::MyCanvas(QWidget* Parent, const QPoint& Position, const QSize& Size) : QSFMLCanvas(Parent, Position, Size, 60) {
sceneUpdater = new SceneUpdater();
text.create(Size.width(), Size.height());
}
void MyCanvas::onInit() {
sceneUpdater->start();
}
void MyCanvas::onUpdate() {
unique_lock<mutex> locker(g_lock_update_component);
g_signal.wait(locker, [&](){return sceneUpdater->isGDone();});
clear(Color::Black);
text.clear(Color::Black);
text.draw(sceneUpdater->getRectangle());
text.display();
draw(Sprite(text.getTexture()));
}
#include "updater.h"
using namespace std;
using namespace sf;
SceneUpdater::SceneUpdater() {
rectangle = RectangleShape(Vector2f(0, 0));
rectangle.setSize(Vector2f(100, 100));
running = gDone = false;
}
bool SceneUpdater::isGDone() {
return gDone;
}
void SceneUpdater::start() {
running = true;
m_thread = thread (&SceneUpdater::run, this);
}
void SceneUpdater::stop() {
running = false;
m_thread.join();
}
void SceneUpdater::run() {
while (running) {
unique_lock<mutex> locker(g_lock_update_component);
gDone=false;
int posX = rand() % 800;
int posY = rand() % 600;
rectangle.setPosition(posX, posY);
gDone = true;
g_signal.notify_one();
this_thread::sleep_for(chrono::milliseconds(100));
}
}
RectangleShape& SceneUpdater::getRectangle () {
return rectangle;
}
#include "mycanvas.h"
#include <ctime>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QApplication>
using namespace std;
int main (int argc, char* argv[]) {
QApplication app (argc, argv);
srand(time(NULL));
QMainWindow *mainWindow = new QMainWindow();
mainWindow->resize(QSize(800, 600));
MyCanvas my_canvas (mainWindow, QPoint(0, 0), QSize(800, 600));
mainWindow->show();
return app.exec();
}
La sfml possède pas mal de limitations, trop même, ce bout de code est vachement plus rapide que d'utiliser un mutex.
Tu devrait quand même impélmenter les variables de conditions dans la SFML.
Et c'est pas une question d'OS vu que quand je fais l'affichage avec Qt, ça marche. ;)
A ce que je sache QTimer utilise un thread non ?