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

Author Topic: Questions and problems with sf::thread  (Read 9377 times)

0 Members and 1 Guest are viewing this topic.

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Questions and problems with sf::thread
« on: August 30, 2012, 04:02:16 pm »
First off I just want to say what I'm making to give you a little better understanding of what it is that I am trying to achieve. I'm trying to make an optimized way of drawing huge amounts of tiled textures onto to screen, or rather an optimized way of storing them and choosing which of them to be drawn onto the screen. So far I've managed to have 3 million 16x16 tiles spread all over my map with an fps of about 1300 - 1400, although it chews up alot of my memory at the moment. To maintain a high fps i render the tiles offscreen to several rendertextures that have the size 1920 x 1088  and draw these onto the screen. My big is that preparing these rendertextures take quite some time, and I preparing them when the texture gets close to the camera. The loading of the texture takes so long that it freezes the screen for the whole duration that it loads. So what i tried is using an sf::thread that is in charge of preparing these textures to be drawn so that the whole program does not freeze, so far I haven't succeeded very well and I have no idea if I'm doing it wrong or if this is the way threads work. Basically the way I'm doing it is this way:


int main ()
{
     sf::RenderWindow app(sf::VideoMode(1920, 1080), "test");

    MyClass myClass;
    sf::thread loadThread(&MyClass::loadTextures, &myClass);
    loadthread.launch();
   
    while ( app.isOpen() )
    {
         // Program here
         // Check for textures that are intersecting the view and have been prepared by the thread
         // Then draw them onto the screen
     }

    return 0;
}

//MyClass.h
class MyClass
{
   public:
   MyClass();
   void loadTextures();

   private:

   bool doLoad;
};

//MyClass.cpp

MyClass::MyClass():
      doLoad(true)
{

}

void MyClass::loadTextures()
{
    while ( doLoad )
    {
          //Check for textures intersecting the view and update these here
     }
}

 

What happens is that the main loop " while ( app.isOpen() ) " freezes when the thread finds a texture to update and starts updating it, the main loop then continues when the updating of the texture has been completed. What I'm trying to do is to make them run in parallel.

I thank you for taking the time to read this! :)

The code above is NOT actual code from my program, this is the basic logic of the draw and texture update parts of my program stripped down to its absolute basics.
« Last Edit: August 30, 2012, 04:11:57 pm by Brodal »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Questions and problems with sf::thread
« Reply #1 on: August 30, 2012, 05:29:50 pm »
This looks ok, so I have the feeling that this piece of code doesn't show the most important things ;)
Laurent Gomila - SFML developer

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Re: Questions and problems with sf::thread
« Reply #2 on: August 30, 2012, 06:02:04 pm »
I dont exactly know how i would reduce this to a minimal example the way it is done now so I'm hoping this will do.

void OptiNode::DrawToTexture()
{

        //Draw all the sprites that this node contains to an offscreen texture,
        //then ready the texture to be drawn by sfml
        for ( int i = 0; i < mNumberOfLayers; i++ )
        {
                //Create a new rendertexture that we can draw to
                sf::RenderTexture *rendTex = new sf::RenderTexture();

                //Copy the rendertexture into the vector holding the rendertextures
                mOptiTextures.push_back(rendTex);

                //Call the create command for it be valid for rendering
                //This is vector containing sf::RenderTexture*
                mOptiTextures[i]->create(mSize.x, mSize.y);
                //Set a view for the rendertexture
                //The view specifies in which area of the screen that it should " look " at
                mOptiTextures[i]->setView(mNodeView);

                //Clear the textures with the color magenta
                //The reason for this is cause it's a weird color that will probably never be used otherwise,
                //which makes it ideal for masking out all the transparent areas of the texture
                mOptiTextures[i]->clear(sf::Color::Magenta);


                for ( std::vector<OptiSprite*>::size_type j = mCurrentSprite; j < mStaticSprites.size(); j++ )
                {
                        if ( mStaticSprites[j]->GetLayer() == i )
                        {
                                mOptiTextures[i]->draw(mStaticSprites[j]->GetSprite());
                        }
                               
                }

                //When all the tiles have been added to the texture, display it
                mOptiTextures[i]->display();

                //Mask away the magenta color to make the texture transparent in the right places
                mMaskImage = mOptiTextures[i]->getTexture().copyToImage();
                mMaskImage.createMaskFromColor(sf::Color::Magenta);

                sf::Texture maskedTexture;
                mMaskedTextures.push_back(maskedTexture);

                mMaskedTextures[i].loadFromImage(mMaskImage);

                sf::Sprite optiSprite(mMaskedTextures[i]);
                optiSprite.setPosition(mPosition);
               
                //Push back the ready sprite contianing the 1920 x 1088 texture into the mOptiSprites vector
                mOptiSprites.push_back(optiSprite);

                //Signal that we are done
                mStatus = READY;

                mIsUnloaded = false;
        }
}
 

This is the code that is being looped in the load texture thread. It is only being done on the nodes that intersect the camera view. From what i have seen it seems that the RenderTexture.Create() function is one of the things making it freeze. But should the whole program freeze while it loads when I'm doing it in a separate thread than the main loop? If not, what could I be doing wrong?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Questions and problems with sf::thread
« Reply #3 on: August 30, 2012, 06:48:07 pm »
I have no idea, this code shouldn't block the main thread.
Laurent Gomila - SFML developer

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Re: Questions and problems with sf::thread
« Reply #4 on: August 30, 2012, 07:03:46 pm »
Can it be because the draw function in the main thread displays the textures that the side thread creates and updates? The main thread does not do any writing or anything it just fetches references to the textures and displays them on the screen. I don't use any mutexes or locks. I'm using a ready flag ( boolean value ) that tells the main thread if the textures are ready to be drawn onto the screen or not so it doesn't try to fetch them while they're being made. I also tried not having a loop in the load thread and instead called the thread.launch() during every frame in the main loop, that produced the same result as having it in a separate thread. I get why the side thread would freeze, as the RenderTexture.create() takes about 0.3 - 0.5 seconds to complete, and all in all 1 loop in the side thread should take about 1.5 seconds. But I dont see why this is affecting my main loop.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Questions and problems with sf::thread
« Reply #5 on: August 30, 2012, 07:27:28 pm »
You should try a similar but minimal code, i.e. a main thread with a standard main loop which draws a certain number of sprites, and a parallel thread that create an equal number of textures.
Laurent Gomila - SFML developer

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Re: Questions and problems with sf::thread
« Reply #6 on: August 30, 2012, 10:27:09 pm »
I tried recreating the problem but with minimal code. The result was the same, while the texture is being prepared the main the whole program goes horribly slow. I can't figure out what is causing this.


#include <SFML/Graphics.hpp>
 
bool doLoad = true;
bool status = false;

std::vector<sf::Sprite> SpriteVector;
sf::RenderTexture *OptiTexture;
sf::Texture MaskedTexture;
sf::Sprite OptiSprite;
sf::Image mMaskImage;

sf::View mNodeView;

void loadTextures()
{
        while ( doLoad )
        {
                //Create a new rendertexture that we can draw to
                if ( !OptiTexture )
                {
                        OptiTexture = new sf::RenderTexture();
                }
                //Call the create command for it be valid for rendering
                //This is vector containing sf::RenderTexture*
                OptiTexture->create(1920, 1080);
                //Set a view for the rendertexture
                //The view specifies in which area of the screen that it should " look " at
                OptiTexture->setView(mNodeView);

                //Clear the textures with the color magenta
                //The reason for this is cause it's a weird color that will probably never be used otherwise,
                //which makes it ideal for masking out all the transparent areas of the texture
                OptiTexture->clear(sf::Color::Magenta);

                for ( std::vector<sf::Sprite>::size_type j = 0; j < SpriteVector.size(); j++ )
                {
                        OptiTexture->draw(SpriteVector[j]);
                }

                //When all the tiles have been added to the texture, display it
                OptiTexture->display();

                //Mask away the magenta color to make the texture transparent in the right places
                mMaskImage = OptiTexture->getTexture().copyToImage();
                mMaskImage.createMaskFromColor(sf::Color::Magenta);

                       
                MaskedTexture.loadFromImage(mMaskImage);

                OptiSprite.setTexture(MaskedTexture);
                OptiSprite.setPosition(0,0);
               
                //Signal that we are done
                status = true;
        }
}

 int main()
 {
     // Create the main window
     sf::RenderWindow window(sf::VideoMode(1920, 1080), "SFML window");
         window.setFramerateLimit(60);
         window.setVerticalSyncEnabled(true);
         sf::View view;
         view.setSize(1920,1080);
     // Load a sprite to display
     sf::Texture texture;
     if (!texture.loadFromFile("dirt.png"))
         return EXIT_FAILURE;
     sf::Sprite sprite(texture);

        mNodeView.setSize(1920,1088);
        mNodeView.setCenter((1920/2),(1088/2));

         for ( int i = 0; i < 10; i++ )
         {
                 for ( int j = 0; j < 10; j++ )
                 {
                         sprite.setPosition(i*16,j*16);
                         SpriteVector.push_back(sprite);
                 }
         }
         status = false;
         
         sf::Thread loadThread(&loadTextures);
         loadThread.launch();
 
     // Start the game loop
     while (window.isOpen())
     {
         // Process events
         sf::Event event;
         while (window.pollEvent(event))
         {
             // Close window : exit
             if (event.type == sf::Event::Closed)
                 window.close();
         }

                  if ( sf::Keyboard::isKeyPressed(sf::Keyboard::W) )
                 {
                         view.move(sf::Vector2f(0,-10));
                 }
                 if ( sf::Keyboard::isKeyPressed(sf::Keyboard::S) )
                 {
                         view.move(sf::Vector2f(0,10));
                 }
                 if ( sf::Keyboard::isKeyPressed(sf::Keyboard::A) )
                 {
                         view.move(sf::Vector2f(-10,0));
                 }
                 if ( sf::Keyboard::isKeyPressed(sf::Keyboard::D) )
                 {
                         view.move(sf::Vector2f(10,0));
                 }
                 
         // Clear screen
         window.clear();
                 window.setView(view);
                 sf::IntRect cameraBounds;
                 cameraBounds.left = view.getCenter().x - (view.getSize().x/2);
                 cameraBounds.top = view.getCenter().y - (view.getSize().y/2);
                 cameraBounds.width = view.getSize().x;
                 cameraBounds.height = view.getSize().y;
                         if ( cameraBounds.intersects(
                                 sf::IntRect(OptiSprite.getPosition().x,
                                 OptiSprite.getPosition().y,
                                 1920,
                                 1088)) )
                         {
                                 if ( status == true )
                                 {
                                        window.draw(OptiSprite);
                                        window.draw(sprite);
                                 }
                         }
         // Update the window
         window.display();
     }
 
     return EXIT_SUCCESS;
 }

 

I hope that you will be able to help me in some way, I've been trying to do this for a couple of days now.
Thank you!

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Re: Questions and problems with sf::thread
« Reply #7 on: August 30, 2012, 10:39:00 pm »
Maybe I'm missing something in your example (late already), but there seems to be no real use for your while loop within the thread of your minimal example. You just loop over and over without ever setting doLoad to false?

Also you're lacking some point where a context switch could occur, so I'd add some sf::Sleep(0) in your drawing loop (not necessarily triggering every iteration).

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Re: Questions and problems with sf::thread
« Reply #8 on: August 30, 2012, 11:25:16 pm »
the reason for it to be just looping over and over again is so that i can see what happens when i move the camera at the same time that it is creating the textures, which is that it is lagging horribly in intervals. Which is the behaviour that I'm trying to get rid of. In my larger application this while loop just redraws when a change has been made to that texture, or if texture no longer exists. What does a context switch do?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Questions and problems with sf::thread
« Reply #9 on: August 30, 2012, 11:36:53 pm »
No problem for me, your code runs smoothly even in debug mode. Are your graphics drivers up-to-date?

And yes, since your example has an infinite loop in the loading thread it should let the CPU breathe with a sf::sleep(/* small value */). A "context" switch is a thread switch. sf::sleep triggers such a switch, when the loading thread goes to sleep the other one can be executed.
Laurent Gomila - SFML developer

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Re: Questions and problems with sf::thread
« Reply #10 on: August 31, 2012, 01:10:28 am »
Did you run my code exactly as is, or did you make any changes to it? because I can't find a way to make it run smooth, the camera moves very choppy when the second thread is making the textures. I've tried reinstalling my graphics drivers but that did no difference at all. Sorry for the quantities or replies that I'm posting but I've been stuck on this for several days and I can't figure out why it works smooth for you but not for me. And thank you very much for the quick replies that I've been getting!

Do i maybe have to make two threads instead of one? one running the main loop and the other loading the textures. Or is it sufficient to just make one sf::thread that loads the textures? Creating 2 sf::threads instead of one poses a new set of problems, where one of them being that the window context is all botched.
« Last Edit: August 31, 2012, 09:43:31 am by Brodal »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Questions and problems with sf::thread
« Reply #11 on: August 31, 2012, 10:31:29 am »
No, your code is ok.

The problem might come from your graphics card; what is it? Are your drivers up to date?
Laurent Gomila - SFML developer

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Re: Questions and problems with sf::thread
« Reply #12 on: August 31, 2012, 10:39:08 am »
my drivers are up to date, downloaded the latest catalyst control center package with all the things in it yesterday. I have an ati radeon hd 5850.

I've investigated what happens a bit more and it seems that the main thread is waiting for the secondary thread when the secondary thread is loading the textures( or at least the main thread stops doing anything while the textures are being loaded ). Don't know why though.

Also, could it matter in which fashion i link sfml? I'm using dynamic linking I think as I have to include all the .dll's in the project folder, does this matter or not?

Also the lag seems to be drastically reduced when i build my project and run it through its executable ( it still drops to about 16 FPS for a very short amount of time though ). Can this problem be related to my IDE? I'm using visual studio 2010 premium.

Not to ask too many questions at the same time but, is there by any chance any way to template the rendertexture, because calling renderTexture.create() everytime a texture needs to be draw seems to be a little heavy, so I'm wondering if there is some way to maybe template it and just assign somethink like a copy ( which I know doesn't work as RenderTexture is a noncopyable ) but something in that direction that can lower the amount of time needed to ready a texture. The reason for throwing away the texture and not reusing it is because it will chew up too much of the memory if I reuse a large amount of textures as I want this to support huge maps.

http://www.youtube.com/watch?v=zTN5Lykt_9I

I made a video of the problem I'm having, it's a little hard to see but if you watch it in fullscreen with 480p activated you can see the stuttering that occurs sometimes. This stuttering is the problem I'm having and it happens when new textures are being created and prepared.
« Last Edit: August 31, 2012, 01:35:22 pm by Brodal »

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Re: Questions and problems with sf::thread
« Reply #13 on: September 01, 2012, 01:55:28 pm »
You don't have to recreate the texture over and over again. Just create it right after you've created its object and you're fine (same for the view).

I'd add a short "sf::Sleep()" to your texture loop, because it might be due to bad luck/scheduling that your render loop simply doesn't have enough time.

Also, what's your CPU? Is it a dual core only?

Brodal

  • Newbie
  • *
  • Posts: 35
    • View Profile
    • Email
Re: Questions and problems with sf::thread
« Reply #14 on: September 01, 2012, 10:27:27 pm »
The thing is that I do delete it mario, because my rendering consist of many many nodes that each have their own textures, so if I don't delete the ones that I do not draw anymore I will run out of ram memory, I'm already up at about 1 gb of ram with 3 million tiles, and that is when I delete the textures I don't use anymore. That is why I have to create new ones, but I'm wondering if there is a more efficient way to do it than that, and not waste ram? And the view is not created more than once, it is just applied to the RenderTexture upon recreating the RenderTexture.

I have added severals sleeps to my texture loop, but that still doesn't solve the problem.
I have an Intel I-7 processor, Quad core. so the CPU shouldnt be the problem.

Thank you for answering, I appreciate the help! :)