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

Author Topic: [2.0] Using a thread to load game resources  (Read 15255 times)

0 Members and 1 Guest are viewing this topic.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #15 on: May 11, 2013, 09:31:53 am »
No.

Can you show a complete and minimal code that reproduces your problem?
Laurent Gomila - SFML developer

BlueCobold

  • Full Member
  • ***
  • Posts: 105
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #16 on: May 11, 2013, 11:00:03 am »
Here you go.
Access violation in ~RenderWindow -> ~Window ->... ~WglContext -> wglDeleteContext()
I assume the destruction of the texture causes the problem, because it is deleted in another thread than in which it had been created in. But I have no such issues in my own engine and I have also no idea how to bypass this in SFML.

#include <SFML/Window/Event.hpp>
#include <SFML/System/Thread.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Texture.hpp>

int main(int argc, char* argv[])
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "access violation in destructor");

    bool loaded = false;

    sf::Texture* tex;
    sf::Thread thread([&](){tex = new sf::Texture(); loaded = tex->loadFromFile("res/img/gui/Window.png");});
// same in this version:
//    sf::Texture* tex = new sf::Texture();
//    sf::Thread thread([&](){loaded = tex->loadFromFile("res/img/gui/Window.png");});

    thread.launch() ;

    while ( window.isOpen() )
    {
        sf::Event event ;
        while ( window.pollEvent(event) )
        {
            if ( event.type == sf::Event::Closed )
                window.close() ;
        }
        if(loaded)
            window.setTitle("Thread done, close now");
    }
    delete tex;

    return 0; // Access violation here - in ~RenderWindow -> ~Window ->... ~WglContext -> wglDeleteContext()
}
« Last Edit: May 11, 2013, 11:29:32 am by BlueCobold »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #17 on: May 11, 2013, 11:52:56 am »
Have you tried a glFlush at the end of the theaded function? If you draw the texture (with a sprite), does it show correctly?
Laurent Gomila - SFML developer

BlueCobold

  • Full Member
  • ***
  • Posts: 105
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #18 on: May 11, 2013, 12:12:59 pm »
glFlush() solves the issue indeed. My bad, thanks a lot!  :D

The texture actually doesn't show in the sample above. In my real code it does.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #19 on: May 11, 2013, 01:02:15 pm »
Ok. This is still a bug that needs to be fixed, but at least now I know exactly what it is.
Laurent Gomila - SFML developer

fallahn

  • Sr. Member
  • ****
  • Posts: 472
  • Buns.
    • View Profile
    • Trederia
Re: [2.0] Using a thread to load game resources
« Reply #20 on: June 18, 2013, 12:49:29 pm »
I've been having a similar problem using sf::RenderTexture. I'm using a thread to load a map file (which works fine) then create a set of entities via an entity manager based on the loaded map data (eg spawn points etc). The entity manager loads the corresponding textures and passes references to them to the entities it creates, which actually draw fine, but entities which use a render texture internally to create their sprite texture do not draw. As far as I can see the actual render texture is not clear/draw/displaying during the entity's draw() call. For example if I add a clear() call immediately after create() with sf::color::green the entity will draw from the main thread as a green sprite... but the entity's draw function should be clearing it yellow.


#include <SFML/system.hpp>
#include <SFML/graphics.hpp>
#include <SFML/window.hpp>

#include <memory>
#include <vector>
#include <atomic>
#include <iostream>

class MyEntity
{
public:
    MyEntity()
    {
        m_renderTexture.create(40u, 40u);
        m_renderTexture.setActive(false); //deactivate because created in another thread
    }
    void Draw(sf::RenderTarget& rt)
    {
        m_renderTexture.clear(sf::Color::Yellow);
        m_renderTexture.display();
        rt.draw(sf::Sprite(m_renderTexture.getTexture()));
    }
private:
    sf::RenderTexture m_renderTexture;
};

class MyEntityManager
{
public:
    MyEntityManager(){};
    void CreateEntities()
    {
        //load resources needed for entity here
        m_entities.push_back(std::unique_ptr<MyEntity>(new MyEntity()));
    }
    void Draw(sf::RenderTarget& rt)
    {
        for(auto ent = m_entities.begin(); ent != m_entities.end(); ++ent)
        {
            MyEntity& myEnt = **ent;
            myEnt.Draw(rt);
        }
    };
private:
    std::vector< std::unique_ptr<MyEntity> > m_entities;
};

//---------------------------------------------------------//

MyEntityManager entManager;
std::atomic<bool> loaded(false);
void ThreadFunc()
{
    //load map data here first

    //create entities based on loaded map data
    entManager.CreateEntities();
    loaded = true;
    std::cout << "Thread Quitting" << std::endl;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800u, 600u), "rt test");
    //MyEntity ent;

    sf::Thread thread(&ThreadFunc);
    thread.launch();

    while(window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }

        //draw test item - this works
        /*window.clear();
        ent.Draw(window);
        window.display();*/


        if(loaded) //drawing this doesn't
        {
          window.clear();
          entManager.Draw(window);
          window.display();
        }
    }
    thread.wait();
    return 0;
}

 

This is using the latest SFML compiled from source, and happens using gcc/mingw and VS11 compilers. I've also tested it on WinXP / nVidia GTS450, Vista64 / Radeon 7970 and Win7-64 / Radeon 7750 with latest drivers and they all exhibit the problem.

I've tried putting break points on the render texture's clear/draw call but in debug mode I get 'expression can not be evaluated' and in release mode VS tells me it can't insert a break point there as the compiler has optimised it out...

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #21 on: June 18, 2013, 01:09:17 pm »
And haven't you tried the suggested fix (glFlush)?
Laurent Gomila - SFML developer

fallahn

  • Sr. Member
  • ****
  • Posts: 472
  • Buns.
    • View Profile
    • Trederia
Re: [2.0] Using a thread to load game resources
« Reply #22 on: June 18, 2013, 01:15:57 pm »
sorry, I forgot to mention that I did, and it made no difference.

fallahn

  • Sr. Member
  • ****
  • Posts: 472
  • Buns.
    • View Profile
    • Trederia
Re: [2.0] Using a thread to load game resources
« Reply #23 on: July 02, 2013, 11:25:29 am »
sorry to bump the thread but I just saw that the glFlush() fix has been included in an update on github. I've recompiled SFML with latest version for both VS11 and MinGW, but it seems it still doesn't fix the render texture problem I posted above.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #24 on: July 02, 2013, 11:32:08 am »
It might be a RenderTexture bug rather than a threading issue.

Could you try to simplify your code (remove classes, just put one render-texture in main()) and get rid of C++11 features (std::atomic)?
Laurent Gomila - SFML developer

fallahn

  • Sr. Member
  • ****
  • Posts: 472
  • Buns.
    • View Profile
    • Trederia
Re: [2.0] Using a thread to load game resources
« Reply #25 on: July 02, 2013, 12:36:17 pm »
Ok, I've narrowed it down a bit. Having a RenderTexture as a member of an object created in a separate thread still has the same problem - the sprite draws green instead of yellow (it works when creating the object in the same thread):

class MyEntity
{
public:
    MyEntity()
    {
        m_renderTexture.create(40u, 40u);
        m_renderTexture.clear(sf::Color::Green);
        m_renderTexture.display();
        m_renderTexture.setActive(false); //deactivate because created in another thread
    }
    void Draw(sf::RenderTarget& rt)
    {
        m_renderTexture.clear(sf::Color::Yellow);
        m_renderTexture.display();
        rt.draw(sf::Sprite(m_renderTexture.getTexture()));
    }
private:
    sf::RenderTexture m_renderTexture;
};

//---------------------------------------------------------//

std::unique_ptr<MyEntity> ent;

bool loaded(false);
void ThreadFunc()
{
    ent = std::unique_ptr<MyEntity>(new MyEntity);
    loaded = true;
    std::cout << "Thread Quitting" << std::endl;
    glFlush();
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800u, 600u), "rt test");

    sf::Thread thread(&ThreadFunc);
    thread.launch();

    while(window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }

        if(loaded) //drawing this doesn't
        {
            window.clear();
            ent->Draw(window);
            window.display();
        }
    }
    thread.wait();
    return 0;
}
 

However, declaring a RenderTexture in the main thread and calling create() on it in a separate thread seems to work:

sf::RenderTexture rt;
bool loaded(false);
void ThreadFunc()
{
    rt.create(40u, 40u);
    rt.clear(sf::Color::Green);
    rt.display();
    rt.setActive(false);
    loaded = true;
    std::cout << "Thread Quitting" << std::endl;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800u, 600u), "rt test");

    sf::Thread thread(&ThreadFunc);
    thread.launch();

    while(window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }

        if(loaded)
        {
            rt.clear(sf::Color::Yellow);
            rt.display();
            window.clear();
            window.draw(sf::Sprite(rt.getTexture()));
            window.display();
        }
    }
    thread.wait();
    return 0;
}
 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #26 on: July 02, 2013, 12:44:54 pm »
So, this code doesn't work:

sf::RenderTexture* rt;
bool loaded(false);
void ThreadFunc()
{
    rt = new sf::RenderTexture;
    rt->create(40u, 40u);
    rt->clear(sf::Color::Green);
    rt->display();
    rt->setActive(false);
    loaded = true;
    std::cout << "Thread Quitting" << std::endl;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800u, 600u), "rt test");

    sf::Thread thread(&ThreadFunc);
    thread.launch();

    while(window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }

        if(loaded)
        {
            rt->clear(sf::Color::Yellow);
            rt->display();
            window.clear();
            window.draw(sf::Sprite(rt->getTexture()));
            window.display();
        }
    }
    delete rt;
    return 0;
}

But this code does:

sf::RenderTexture* rt;
bool loaded(false);
void ThreadFunc()
{
    rt->create(40u, 40u);
    rt->clear(sf::Color::Green);
    rt->display();
    rt->setActive(false);
    loaded = true;
    std::cout << "Thread Quitting" << std::endl;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800u, 600u), "rt test");

    rt = new sf::RenderTexture;
    sf::Thread thread(&ThreadFunc);
    thread.launch();

    while(window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }

        if(loaded)
        {
            rt->clear(sf::Color::Yellow);
            rt->display();
            window.clear();
            window.draw(sf::Sprite(rt->getTexture()));
            window.display();
        }
    }
    delete rt;
    return 0;
}

Right?
« Last Edit: July 02, 2013, 01:16:09 pm by Laurent »
Laurent Gomila - SFML developer

fallahn

  • Sr. Member
  • ****
  • Posts: 472
  • Buns.
    • View Profile
    • Trederia
Re: [2.0] Using a thread to load game resources
« Reply #27 on: July 02, 2013, 12:54:23 pm »
Hmm. Actually apart from changing it to

rt->getTexture()
 

(just a typo, I know) they both work for me.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #28 on: July 02, 2013, 01:20:22 pm »
Hmm... I can't find any other major difference between my first code and yours, the code is the same I just removed the Entity class and copied the contents of its functions wherever they were called :-\
Laurent Gomila - SFML developer

fallahn

  • Sr. Member
  • ****
  • Posts: 472
  • Buns.
    • View Profile
    • Trederia
Re: [2.0] Using a thread to load game resources
« Reply #29 on: July 02, 2013, 02:10:20 pm »
AHA. It seems that the problem occurs if I call rt->clear() *after* window.clear(). If I clear the RenderTexture first it works ok. This is only the case when rt is created in a separate thread. Create it in the main thread and it works fine whether it's cleared before or after window.

So your example:
            rt->clear(sf::Color::Yellow);
            rt->display();
            window.clear();
            window.draw(sf::Sprite(rt->getTexture()));
            window.display();
 

works fine (both creating in a separate and the main thread). On the other hand:

            window.clear();
            rt->clear(sf::Color::Yellow);
            rt->display();        
            window.draw(sf::Sprite(rt->getTexture()));
            window.display();
 

or

            window.clear();
            entity.Draw(window);
            window.display();
 

only works when the RenderTexture / entity object are created in the main thread.