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

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

0 Members and 1 Guest are viewing this topic.

NPS

  • Newbie
  • *
  • Posts: 31
    • View Profile
[2.0] Using a thread to load game resources
« on: January 20, 2013, 03:03:26 am »
I'm making a loading screend and I thought I needed a second thread to load the game resources in. In the meantime the main thread would keep running main game loop, handling events, showing "loading" image and keeping window responsive. So the second thread only loads the resources and then ends.

For simplicity's sake (if I simplify my problem too much and you won't be able to help me then I'll go into more detail and paste some code but I'd like to avoid that) let's say a have this big Game class which has all resources as fields and and auxiliary private Load() method (which only loads these resources, nothing else). My game is very small so one big class is enough, actually. Anyway, in the main thread I call a Game's method (let's call it PublicLoad()) which starts a new thread giving it its Load() method and "this" pointer, then I keep running the main loop until the data are loaded (I have a boolean variable "loaded" which is set to "true" by the 2nd thread when the loading is done and the main thread keeps checking this variable).

After all that, the Game class should start "playing" the game using the loaded resources. But this doesn't work as inteded, as I can see some of the resources loaded and used, and others not (I can hear the music and sounds, I can see some images but not others). Although, after checking the resources with debugger everything seems loaded and fine). Just nothing shows up on the screen.

Also actually the only things that I can't see on my screen are classes using sf::RenderTexture, I don't know if it's related to using threads in anyway.

Oh yeah, and the same Game class works fine when I'm loading using only the main thread.

Edit: Ok, it's definitely something with sf::RenderTexture, because if I draw directly into sf::RenderWindow instead of sf::RenderTarget (and then from it to the window) everything works fine.

Any idea? Should I post some code (what code would be helpful here)?
« Last Edit: January 20, 2013, 11:31:24 am by NPS »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #1 on: January 21, 2013, 10:06:04 am »
What's your graphics card? You should try the latest version of your graphics driver, and the latest sources of SFML.
Laurent Gomila - SFML developer

NPS

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #2 on: January 21, 2013, 11:16:22 am »
I have GTX 460. Is "SFML 2.0 snapshot" the latest version?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #3 on: January 21, 2013, 11:56:02 am »
Quote
Is "SFML 2.0 snapshot" the latest version?
Yes.
Laurent Gomila - SFML developer

NPS

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #4 on: January 21, 2013, 12:15:43 pm »
So I am using latest graphics driver and sources version.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #5 on: January 21, 2013, 12:21:23 pm »
So you should post a complete and minimal code that reproduces your problem.
Laurent Gomila - SFML developer

NPS

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #6 on: January 21, 2013, 01:03:08 pm »
It would be something like this. The number on the left (1337) displays fine, the number on the right (7331, loaded in a separate thread) doesn't show up at all.

main.cpp:
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include "DigitDisplay.hpp"

typedef sf::Vector2<float> float2;

sf::RenderWindow window;
DigitDisplay numberNormal;
DigitDisplay numberThread;

void LoadResources(void)
{
    numberThread.Load("data/digits.png", "7331");
}

int main(void)
{
    numberNormal.Load("data/digits.png", "1337");
    numberNormal.SetPosition(float2(300.0f, 300.0f));
    sf::Thread thread(LoadResources);
    thread.launch();
    thread.wait();
    numberThread.SetPosition(float2(600.0f, 300.0f));

    window.create(sf::VideoMode(800, 600, 32), "SFML Framework",
        sf::Style::Titlebar | sf::Style::Close);
    window.setVerticalSyncEnabled(true);
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
        }
        window.clear(sf::Color::Black);
        numberNormal.Render();
        numberThread.Render();
        window.display();
    }
    return EXIT_SUCCESS;
}
 
DigitDisplay.hpp:
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>

typedef unsigned int uint;
typedef sf::Vector2<int> int2;
typedef sf::Vector2<float> float2;

extern sf::RenderWindow window;

class DigitDisplay
{
    private:
    sf::Texture digitImage;
    std::vector<sf::Sprite> digits;
    sf::RenderTexture renderTexture;
    sf::Sprite textureSprite;
    std::string text;
    float2 position;

    void CreateRenderTexture(void);
    inline int GetDigit(char c);

    public:
    void Load(std::string filename, std::string _text = "");
    void Render(void);
    void SetPosition(float2 _position);
};
 
DigitDisplay.cpp:
#include "DigitDisplay.hpp"

void DigitDisplay::CreateRenderTexture(void)
{
    int2 size(digitImage.getSize().x * text.size() / 10, digitImage.getSize().y);
    renderTexture.create(size.x, size.y);
    textureSprite.setTexture(renderTexture.getTexture());
    textureSprite.setTextureRect(sf::IntRect(0, 0, size.x, size.y));
}

int DigitDisplay::GetDigit(char c)
{
    switch (c)
    {
        case '0':
            return 0;
        case '1':
            return 1;
        case '2':
            return 2;
        case '3':
            return 3;
        case '4':
            return 4;
        case '5':
            return 5;
        case '6':
            return 6;
        case '7':
            return 7;
        case '8':
            return 8;
        case '9':
            return 9;
        default:
            return -1;
    }
}

void DigitDisplay::Load(std::string filename, std::string _text)
{
    digitImage.loadFromFile(filename);
    uint digitWidth = digitImage.getSize().x / 10;
    uint frameHeight = digitImage.getSize().y;
    digits.resize(10);
    for (int i = 0 ; i < 10 ; ++i)
    {
        digits[i].setTexture(digitImage);
        digits[i].setTextureRect(sf::IntRect(i * digitWidth, 0, digitWidth, frameHeight));
    }
    text = _text;
    if (text.size())
    {
        CreateRenderTexture();
    }
}

void DigitDisplay::Render(void)
{
    renderTexture.clear(sf::Color::Transparent);
    float digitWidth = digitImage.getSize().x * 0.1f;
    for (uint i = 0 ; i < text.size() ; ++i)
    {
        int digit = GetDigit(text[i]);
        digits[digit].setPosition(i * digitWidth, 0.0f);
        renderTexture.draw(digits[digit]);
    }
    renderTexture.display();
    textureSprite.setPosition(position);
    window.draw(textureSprite);
}

void DigitDisplay::SetPosition(float2 _position)
{
    position = _position;
}
 

======================================================================
Edit: I've made some progress. I found out that when I call DigitDisplay::CreateRenderTexture() from the 2nd thread nothing shows up on the screen. However, when I call it from the main thread (after loading the image and setting up sf::Sprite in DigitDisplay in 2nd thread) the image shows up on the screen and everything seems to be working fine.

Can't I use sf::RenderTexture::create() in another thread? If I can, how to do it correctly?
« Last Edit: January 21, 2013, 02:06:44 pm by NPS »

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #7 on: January 21, 2013, 06:35:05 pm »
You don't exactly get the concept of minimal code, do you?

The following exhibits similar behavior for me.

#include <SFML/Graphics.hpp>

struct LoadTexture
{
    sf::Texture& texture ;
    bool & success ;
    std::string filename ;

    LoadTexture(sf::Texture& t, bool& s, const std::string& fname)
        : texture(t), success(s), filename(fname) {}

    void operator()()
        { success = texture.loadFromFile(filename) ; }
};

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

    bool success = false ;
    sf::Texture green ;

    {
        sf::Thread thread(LoadTexture(green, success, "GreenTile.png")) ;
        thread.launch() ;
    }
//    success = green.loadFromFile("GreenTile.png") ;

    if ( !success )
        return 0 ;

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

        window.clear() ;
        window.draw(sf::Sprite(green)) ;
        window.display() ;
    }
}

Been awhile since I've updated to the latest SFML sources.  I'll do that later today.
« Last Edit: January 21, 2013, 06:37:54 pm by cire »

NPS

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #8 on: January 21, 2013, 06:59:42 pm »
You don't exactly get the concept of minimal code, do you?
I didn't want to cut out anything relevant (and trust me - I've cut out a lot already). I my case the problem is with sf::RenderTexture (vide my last post's edit note). I don't see any sf::RenderTexture in your code. :P

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #9 on: January 21, 2013, 08:34:14 pm »
I didn't want to cut out anything relevant (and trust me - I've cut out a lot already).

Instead of worrying about cutting out anything "relevant" you cut it down to minimal.  If the problem isn't still exhibited, you know it was something that you left out.  Add stuff back in until you find what triggers the issue.  Now you have minimal code that still reproduces the error.


I my case the problem is with sf::RenderTexture (vide my last post's edit note). I don't see any sf::RenderTexture in your code. :P

Would you imagine that sf::RenderTexture uses sf::Texture internally?

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #10 on: January 22, 2013, 06:10:31 am »
Updating to the latest source had no effect.

I did manage to get it  working using the following (Windows specific) code, though.

#include <Windows.h>
#include <thread>
#include <SFML/Graphics.hpp>

struct LoadTexture
{
    sf::Texture& texture ;
    bool & success ;
    std::string filename ;

    LoadTexture(sf::Texture& t, bool& s, const std::string& fname )
        : texture(t), success(s), filename(fname) {}

    void operator()()
    {  
        wglMakeCurrent(wglGetCurrentDC(), wglGetCurrentContext()) ;
        success = texture.loadFromFile(filename) ;
        wglMakeCurrent(NULL, NULL) ;
    }
};

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

    bool success = false ;
    sf::Texture green ;

    {
        sf::Thread thread(LoadTexture(green, success, "GreenTile.png")) ;
        thread.launch() ;
    }


//    success = green.loadFromFile("GreenTile.png") ;

    if ( !success )
        return 0 ;

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

        window.clear() ;
        window.draw(sf::Sprite(green)) ;
        window.display() ;
    }
}

[edit:
And now I see sf::Context which somehow escaped me when I looked earlier.  That would provide a much better solution.]
« Last Edit: January 22, 2013, 06:32:43 am by cire »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: [2.0] Using a thread to load game resources
« Reply #11 on: January 22, 2013, 08:48:31 am »
SFML is supposed to activate a context whenever it needs one, you should'nt bave to worry about that. I'm currently on vacation so I won't be able to help more, sorry.
Laurent Gomila - SFML developer

NPS

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #12 on: January 22, 2013, 06:27:10 pm »
So as I've said, now I'm calling sf::RenderTexture::create() method from the main thread and that is working fine on my PC (nvidia card). But later I tried running the same code on my laptop (ati card) and 2 things happen: I don't see images using sf::RenderTexture (as was the case initially on my PC) and also the application crashes after closing the window saying something about uncaught exception and "access violation reading location...". :P

NPS

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #13 on: January 24, 2013, 08:45:46 pm »
It gets worse - on my laptop I always get said error when loading using another thread - not only with sf::RenderTarget. :P

BlueCobold

  • Full Member
  • ***
  • Posts: 105
    • View Profile
Re: [2.0] Using a thread to load game resources
« Reply #14 on: May 10, 2013, 08:54:47 pm »
Is there any news on that? I have a similar issue on 2 of 3 PCs with that. Loading all works fine, but when closing the Window and internally wglDeleteContext gets called, an access violation is thrown. When loading stuff without using a thread, everything is fine. Loading from within an sf::Thread causes trouble.
(ATI card)
I created other games with GL before and also loaded my stuff in separate threads and never had that issue there.
« Last Edit: May 10, 2013, 08:56:36 pm by BlueCobold »