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

Author Topic: sf::Font and sf::Text  (Read 2634 times)

0 Members and 1 Guest are viewing this topic.

fjanisze

  • Newbie
  • *
  • Posts: 8
    • View Profile
sf::Font and sf::Text
« on: December 11, 2013, 02:59:08 pm »
Hello,

There's something i dont understand about how the sf::Font and sf::Text objects works, let's assume that we have two threads, A and B. The thread A load the font in a local variable and then enter a loop in which just draw a text, the text -sf::Text- we can safely assume that is a member variable of a class.

Something like this -pseudocode-:

class foo
{
    sf::Font font;
    sf::Text text;
    sf::RenderWindow window;
public:
    foo();
    void set_text( const string& text );
    void loop();
};

where:

foo::foo()
{
   text.setString("THIS IS A TEXT");
   //Set all the other stuff, like position, color ecc ecc
   std::thread run_me( &f , this ); //Run the the function 'loop' in a separate thread.
}

void foo::loop()
{
    //Load the font
    while( )
    {
        //Bla bla
        window.draw( text );
        //bla bla
        //Wait for a while, this_thread::sleep_for
    }
}

Then we have:

int main()
{
    foo f; //the thread is spawned and 'loop' is running, the window shows 'THIS IS A TEXT'
    //Sleep for 1 sec
    f.set_text("THIS NOT WORK");
   //Continue showing the text
}

Let assume that all the missing code is present, the class 'foo' is thread safe and no dangerous operation are done, the question is how is it possible that the second string printed is not THIS NOT WORK but: "THIS   T".

It looks like the character which are printed in the second case are the one in common with the first string already printed, but no other. Why?

If the first text printed is ABC and i want to print in the second BCD then only BC is printed..

Thanks

Thanks

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: sf::Font and sf::Text
« Reply #1 on: December 11, 2013, 03:13:19 pm »
Because multi threaded environments are not easy. ;)

There are already a few similar posts floating around on the forum. The solution usually was to call glFlush() before rendering the text, that way one makes sure everything got in.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

fjanisze

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: sf::Font and sf::Text
« Reply #2 on: December 12, 2013, 08:20:24 am »
I was looking for a similar post in the forum, but i get this error "Please try again. If you come back to this error screen, report the error to an administrator." when i look for something in the forum  :-[

Anyway, where should i put the call to glFlush()? I tried in several way with no success. Have a look at this crap code below:


class foo
{
    sf::Font font;
    sf::Text text;
    std::thread th;
    sf::RenderWindow window;
public:
    foo()
    {
        text.setString("THIS IS A PRINT");
        text.setCharacterSize(12);
        text.setStyle( sf::Text::Regular );
        text.setColor( sf::Color::Green );
        text.setPosition( sf::Vector2f( 0, 0 ) );
        //Run the thread
        std::thread myth( &foo::loop, this );
        th = std::move( myth );
    }
    ~foo()
    {
        th.join();
    }
    void loop()
    {
        window.create(sf::VideoMode(300, 100), "My window");
        if( !font.loadFromFile("consola.ttf") )
        {
            std::cerr<<"FONT LOAD ERROR\n";
            std::terminate();
        }
        text.setFont( font );

        while (window.isOpen())
        {
            window.clear(sf::Color::Black);
            sf::Event event;
            while (window.pollEvent(event))
            {
                // "close requested" event: we close the window
                if (event.type == sf::Event::Closed)
                    window.close();
            }

            window.draw( text );
            window.display();
            std::this_thread::sleep_for( std::chrono::milliseconds{ 100 } );
        }
    }
    void setstring( const std::string& str )
    {
        text.setString( str.c_str() );
    }
};

int main()
{
    foo myfoo;
    std::this_thread::sleep_for( std::chrono::milliseconds{ 1000 } );
    //Change the string.
    myfoo.setstring("NOW CHANGE THE STRING");
    return 0;
}

This look very bad but is sufficient to reproduce the problem, when i put the call to glFlush before the window.display the output is:

"THIS IS A PRINT"

and

"NOW CHANG  TH  STRING"

The 'E' for some reason is missing..

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
AW: sf::Font and sf::Text
« Reply #3 on: December 12, 2013, 08:39:19 am »
To search with the forum search function you need to deslecte some subforums below the search bos. I still hope Laurent will take his time someday to improve/fix this...
Alternatively you can search with Google and limit the search with "site: en.sfml-dev.org".

I'd say you need to place it right before the draw call. Btw are you making sure that there's no race condition around your variables?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

fjanisze

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: sf::Font and sf::Text
« Reply #4 on: December 12, 2013, 09:28:09 am »
I made a small change in the code, now the output is even more odd.. First of all, a mutex protect the sf::Text variable from data races, and now in the 'main' function i wait for some time before changing the string with setString:

class foo
{
    sf::Font font;
    sf::Text text;
    std::thread th;
    sf::RenderWindow window;
    std::mutex mt;
public:
    foo()
    {
        text.setCharacterSize(12);
        text.setStyle( sf::Text::Regular );
        text.setColor( sf::Color::Green );
        text.setPosition( sf::Vector2f( 0, 0 ) );
        //Run the thread
        std::thread myth( &foo::loop, this );
        th = std::move( myth );
    }
    ~foo()
    {
        th.join();
    }
    void loop()
    {
        window.create(sf::VideoMode(300, 100), "My window");
        if( !font.loadFromFile("consola.ttf") )
        {
            std::cerr<<"FONT LOAD ERROR\n";
            std::terminate();
        }
        text.setFont( font );

        while (window.isOpen())
        {
            window.clear(sf::Color::Black);
            sf::Event event;
            while (window.pollEvent(event))
            {
                // "close requested" event: we close the window
                if (event.type == sf::Event::Closed)
                    window.close();
            }
            glFlush();
            mt.lock();
            window.draw( text );
            window.display();
            mt.unlock();
            std::this_thread::sleep_for( std::chrono::milliseconds{ 50 } );
        }
    }
    void setstring( const std::string& str )
    {
        std::lock_guard< std::mutex > lock( mt );
        text.setString( str.c_str() );
    }
};

int main()
{
    foo myfoo;
    std::this_thread::sleep_for( std::chrono::milliseconds{ 200 } );
    //Change the string.
    myfoo.setstring("THIS IS A TEST");
    std::this_thread::sleep_for( std::chrono::milliseconds{ 2500 } );
    myfoo.setstring("DO NOT WORK ");
    std::this_thread::sleep_for( std::chrono::milliseconds{ 2500 } );
    return 0;
}

At first the instance of foo is created and the thread runs, after 200ms a call to sestring set a new string in the sf::Text variable, then the app wait 2500ms, in this time something should be displayed but only dark is visible, 'THIS IS A  TEST' is not even printed. after 2500ms the second string is printed, but without the 'K'

"DO NOT WOR ".

This is pretty strange to me, if i remove the first 200ms waiting point, the first string is properly printed, but the second one is still without the 'K'.

The glFlush call is right before the 'draw' and 'display' call.

Any idea on what may be wrong here?  ???

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: sf::Font and sf::Text
« Reply #5 on: December 12, 2013, 09:54:25 am »
When posting code you should make use of the [ code=cpp ] tag. ;)

Why are you using different lock methods in loop and setString?
I suggest to always use RAII for locks, so you can be sure that the lock will get releases as soon as you leave the scope. Note: you can create your own scopes without new functions by using a block {}.

Also make sure to call glFlush within the critical zone, because if you 1) flush 2) change the text 3) lock 4) draw, the flush might have not flushed the full text.

Overall I have to ask, why do want to use multiple threads. It's really not that easy and you can't really separate the rendering and updating, since you'd be locking things as you do now, thus creating again a sequential application instead of the parallel one. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

fjanisze

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: sf::Font and sf::Text
« Reply #6 on: December 12, 2013, 11:25:31 am »
I will use the cpp tag next time :)

For a reference about lock_guard please have a look at http://en.cppreference.com/w/cpp/thread/lock_guard.

No matter where i put the glFlush the thing is not working and the string appearing.

What make me very confused is why if i remove the 200ms time point the first string is printed, is this related to the underlying opengl library or SFML?

About why the app is multithreaded instead of single threaded, well, for performance reason! The problem appear in a multithreaded economic simulator of about 10k LOC, here i'm trying just to identify the reason of the malfunction to apply the 'fix' in the real application.  :P

fjanisze

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: sf::Font and sf::Text
« Reply #7 on: December 12, 2013, 12:40:53 pm »
I have an update about this issue, seems that the call to glFlush() need to be performed in the thread which change the string not in the loop which perform the draw/display sequence.

If after each setstring, the glFlush function is invoked everything works properly.

To summarize, in case of parallel threads i need to refresh the context with glFlush in the thread's where a modification of the SFML structure is done, in this case in the thread which modify the sf::Text.

 8)