Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Events - help.  (Read 1913 times)

0 Members and 1 Guest are viewing this topic.

MrGarrison

  • Newbie
  • *
  • Posts: 6
    • View Profile
Events - help.
« on: June 15, 2023, 03:44:34 pm »
Hi everybody,

First, i would love to say that i use to develop a lot of stuff using SFML a few years ago, all following docs from your page, but now i have a problem that i don't know how to resolve.

I was making kind of presentation/tutorial of SFML, and now i am in a bit of problem because i have request to make a one example of using SFML with full event driven game loop (not optimized "busy wait" loop with controlling number of FPS). As a saw, all stuff like - setFrameLimit, vsync, or manually customized with timers - are giving good performance with small CPU usage, but still, request is - make one example with full event driven loop ( wait for event - i know that there is - WaitForEvent function, but i was using it only in some situations in combination with PollEvent, like pauses etc ).

Is it possible for someone of you to help me, or make basic example with just drawing screen or something trivial on it like square ?

Thank you for reading.


eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11043
    • View Profile
    • development blog
    • Email
Re: Events - help.
« Reply #1 on: June 15, 2023, 05:49:24 pm »
What's their definition of "event driven" exactly?

SFML's API isn't event driven, even using WaitForEvent isn't really event driven.
But you can design an API around the SFML API, that pushes the SFML events into an event bus or similar, on top of which you then build your "event-driven" application.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

MrGarrison

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Events - help.
« Reply #2 on: June 16, 2023, 02:57:17 am »
Well, as i could understand, something that is not based on busy-wait loop, like normal gameloops.

Loop that is event driven ( even if you have actions that are automatic like enemies in game - everything should be event driven, not classic - poll event - update, render etc ... ).

SFML's API isn't event driven, even using WaitForEvent isn't really event driven.

Well i know that, but i don't have a right understanding from the other side about that...

Quote
But you can design an API around the SFML API, that pushes the SFML events into an event bus or similar, on top of which you then build your "event-driven" application.

Anybody knows how can i do that ?
I need simple example if it is possible.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11043
    • View Profile
    • development blog
    • Email
Re: Events - help.
« Reply #3 on: June 16, 2023, 08:07:03 am »
Maybe check again with whoever made the request, so you have the same understanding, what they wanted.

Using a message bus or similar, isn't exactly "simple", so don't expect simple examples.
There are many ways how to implement stuff and you'll also notice that a lot of time more advanced topics such as ECS get intermixed at this level.

Tank ones wrote a pretty good chapter on Message Bus and I've once implemented a relatively simple message bus / publish-subscriber setup, with SFML example.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

MrGarrison

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Events - help.
« Reply #4 on: June 23, 2023, 12:45:42 am »
So i'll update this topic.

It seems that request is to make game loop to be fully depend on WaitEvent, not PollEvent.

I dont know if it is the best approach, but mine presentation wouldn't even be considered for review if i don't "fix it", because it had been sad - that "busy wait pollEvent" method is not 21st century way of programming (wtf, i know but ... it is what it is ).

I saw some implementation to make it at least to "look like" some kind of WaitEvent driven code, so, it looks like this:

Quote
#include <chrono>
#include "pch.h"
#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->setActive(true);
      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("Data/kenny.png");
   sprite = sf::Sprite(texture);
   sf::RenderWindow window(sf::VideoMode(400, 200), "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();
}

So, outcome would be moving of little Kenny on canvas - problem is like in one of previous subjects - he is moving only when there are events (halted without it), and + it is moving slow, in not regular steps that are defined by sleep method.

I know that first halted stuff could be solved with better implementation of setActive(true/false), but where i should put it in this code to make it work ?

Can somebody help me to solve these problems?
« Last Edit: June 23, 2023, 12:48:21 am by MrGarrison »

Hapax

  • Hero Member
  • *****
  • Posts: 3383
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Events - help.
« Reply #5 on: June 23, 2023, 02:54:10 am »
If you want a thread running fully for the game loop, can it not be the main thread?

That said, if you want a separate thread that does all the work, you should be able to do that by sending information to it whenever the main thread gets events.

It's worth noting but probably obvious that you shouldn't really be doing stuff to the window if the other thread is doing that.

Multi-threading is complicated so this may be wrong but I'm imagining something (slightly pseudo) like:
std::atomic<std::size_t> command{ 0u };
std::mutex windowMutex;

void game(sf::RenderWindow& window)
{
        window.setActive(true);
       
        bool isRunning{ true };
        bool isPaused{ false };
        while (isRunning)
        {
                if (command > 0u)
                {
                        switch (command)
                        {
                        case 1u: // quit
                                isRunning = false;
                                break;
                        case 2u: // pause
                                isPaused = true;
                                command = 0u;
                                break
                        case 3u: // unpause
                                isPaused = false;
                                command = 0u;
                                break
                        }
                }
               
               
               
                if (!isPaused)
                        updateStuff();
               
               
               
                while (windowMutex.isLocked()) { }
               
                windowMutex.lock();
                window.clear();
                window.draw(things);
                window.display();
                windowMutex.unlock();
        }
       
        while (windowMutex.isLocked()) { }
       
        windowMutex.lock();
        window.close();
        windowMutex.unlock();
}

int main()
{
        std::thread game;
       
        sf::RenderWindow window(sf::VideoMode(1920u, 1080u), "");
        window.setFramerateLimit(300u);
        window.setActive(false);
       
        bool quit{ false };
        bool isPaused{ false };
        while (!quit)
        {
                sf::Event event;
                if (window.waitEvent(event))
                {
                        switch (event.type)
                        {
                        case sf::Event::Type::Closed:
                                quit = true;
                                command = 1u;
                                break;
                        case sf::Event::Type::KeyPressed:
                                switch (event.key.code)
                                {
                                case sf::Keyboard::Escape:
                                        isPaused = !isPaused;
                                        command = isPaused ? 2u : 3u;
                                        break;
                                }
                                break;
                        }
                }
        }
       
        game.join();
}
 

,which, it turns out, is very similar to your example code. One difference is that I haven't locked the mutex during waitEvent otherwise it stays locked while asleep and your other thread is halted.
I'm not sure how thread-safe sf::Window is in itself. Maybe at least waitEvent is thread-safe; the documentation suggests this approach anyway.
This function is typically used when you have a thread that is dedicated to events handling: you want to make this thread sleep as long as no new event is received.
sf::Event event;
if (window.waitEvent(event))
{
   // process event...
}

Also, the thread should already be sleeping itself with setFramerateLimit; there should be no need to sleep it manually as well.

Setting a window as active is only for the targetting of rendering. Since nothing is being rendered in the main thread, it shouldn't need to be activated.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

MrGarrison

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Events - help.
« Reply #6 on: June 23, 2023, 10:20:14 am »
Thank you for fast and deep response.

Well, it was not mine desire to be thread running fully, but it was the only stuff that i came up with so i can make game loop to be without pollEvent - just waitEvent ...

I saw your snippet, so - simply i have stayed on mine own, but with that significant change - without locking during waitEvent, and animation works fine.

On the other side - i know how setFramerateLimit works, but it seems that request side is kind a hard to work with - so i'll see if i am able to stick with setFramerateLimit only, or i'll need to use sleep directly.

Quote
I'm not sure how thread-safe sf::Window is in itself. Maybe at least waitEvent is thread-safe; the documentation suggests this approach anyway.

I have found this old topic, so i dunno if some new problems can occur, since the finale of that topic from 2017 was:

Quote
I added some labels but I'm not really sure where we ended up with the discussion.

SFML isn't thread-safe
waitEvent() and close() in its current state can't be used in a multi-threaded environment
SFML uses a "custom" waitEvent loop
Do we do something about this or do we just mark it as "won't fix"?

Whole story is here:

https://github.com/SFML/SFML/issues/1184


Hapax

  • Hero Member
  • *****
  • Posts: 3383
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Events - help.
« Reply #7 on: June 23, 2023, 09:59:18 pm »
Thank you for fast and deep response.
You're welcome. Hope it could spark some inspiration to fix it as I'm not massively experienced with multi-threading myself.

On the other side - i know how setFramerateLimit works, but it seems that request side is kind a hard to work with - so i'll see if i am able to stick with setFramerateLimit only, or i'll need to use sleep directly.
As an extra bit of information - and IIRC - the framerate limit is created by sleeping the thread and that that is done when calling display().

Quote
I'm not sure how thread-safe sf::Window is in itself. Maybe at least waitEvent is thread-safe; the documentation suggests this approach anyway.

I have found this old topic, so i dunno if some new problems can occur
Again, I'm not sure of all the details of how SFML's window works with multiple threads but...
Maybe using waitEvent is pretty safe...
It basically just sleeps. And then, is only checking for an event so shouldn't interfere with anything else, especially since events are touched in other threads.

Note that in my example, the only time the main thread touches the window is to grab an event when there is one so maybe event the mutexes (mutices?) are not necessary in that case.

Also note that the window is closed by the 'other' thread but only after been told to by the main one and that one immediately quits its loop so it won't be calling waitEvent on the window when it's closed.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*