SFML community forums

Help => Graphics => Topic started by: lzr on April 30, 2008, 07:50:51 am

Title: [Solved] Loading Images Inside a Thread
Post by: lzr on April 30, 2008, 07:50:51 am
I am in the long process of porting a game from Direct3d/Win32 to SFML, and before I did all my loading on a separate thread, while a loading animation played in the main thread. For some reason, all the images I load while in the separate thread come out as blank, white, boxes. Is there any way to fix this?
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on April 30, 2008, 09:15:20 am
That's because your OpenGL context is bound to the main thread, thus any OpenGL call occuring in the secondary thread will fail (the standard error output should be full of messages).

The solution would be to unbind the OpenGL context from the main thread (Window::SetActive(false)), bind it to the secondary one and load your images. But it wouldn't work in this case because you still need the context in the main thread to display your animation.

You would be able to do what you want if OpenGL was not restricted to one thread at a time, but that's not the case.
Title: [Solved] Loading Images Inside a Thread
Post by: workmad3 on May 01, 2008, 08:50:39 am
Hmm... considering how multi-threaded SFML is and designed to be, wouldn't it be prudent to provide an 'OGL wrapper thread' that can track OGL drawing and state calls in a queue and get all the OGL calls done in a single thread to avoid issues like this? I was just considering the possibility of writing my own but then realised it would have problems with SFML code which would be making it's own calls to OGL in the same period.
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 01, 2008, 10:10:50 am
Hmm... and how would you do that ?
Title: [Solved] Loading Images Inside a Thread
Post by: lzr on May 02, 2008, 01:11:47 am
I think what workmad3 is suggesting is that you move all OpenGL operations to a completely separate thread that watches a queue that contains a list of the operations that need doing. This would work, but could complicate a lot. Would all the OpenGL calls then become asynchronous? That actually might be kind of cool.

Another thing that might work (I don't know, I've never tried) is to lock an in-engine mutex before every OpenGL command. That way OpenGL is never called in two threads at once and you can ensure that it is always activated in the current thread. Since most of the loading time isn't spent calling OpenGL, the loading animation would still play for most of the time.

I do think that since the engine supports threads, there should be some internal handling of problems like this.

BTW, thanks for your hard work, sorry if I'm asking for too much.
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 02, 2008, 05:06:52 am
Quote
I think what workmad3 is suggesting is that you move all OpenGL operations to a completely separate thread that watches a queue that contains a list of the operations that need doing. This would work, but could complicate a lot. Would all the OpenGL calls then become asynchronous? That actually might be kind of cool.

That would definitively be complicated and even might slowdown global performances.

Quote
Another thing that might work (I don't know, I've never tried) is to lock an in-engine mutex before every OpenGL command. That way OpenGL is never called in two threads at once and you can ensure that it is always activated in the current thread. Since most of the loading time isn't spent calling OpenGL, the loading animation would still play for most of the time

I can't bind OpenGL to a thread if you haven't first unbound it from the other thread.

In conclusion, I can't help much about threading issues, as it's highly related to pure OpenGL stuff.
Title: [Solved] Loading Images Inside a Thread
Post by: workmad3 on May 03, 2008, 06:12:18 pm
Its a common problem with OGL and one of the 'standard' solutions is as I described... all OGL operations need to be in a single thread, so a complete OGL wrapper is created to do this... it provides wrapper functions for all OGL calls (close to the normal OGL names) or even a more flexible system that allows extensions as well, and queues them all up to be executed in a single thread.

The reason I suggest it as part of SFML is because SFML itself uses OGL, so any third party solution would either need to rewrite some SFML stuff to use the wrapper, or deal with issues of SFML not using the wrapper.

As for complexity and performance... yes it would increase complexity and decrease performance slightly, but it would increase the ease-of-use of the library when users are integrating with OGL themselves.

It would still have problems if people ignored the OGL wrapper as then you would have issues with 2 threads still (especially if the user binds OGL to their own thread, meaning SFML loses itscontrol of the API) but as there can be worse consequences on some peoples machines than just blank screens (I've heard it can do things like corrupt the stack and crash applications and possibly even the computer), it may be worthwhile to investigate possible solutions to the multi-threaded OGL problem. Of course, all these problems may be solved when OGL 3.0 is finally released and implementations appear :)
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 03, 2008, 06:29:03 pm
What about the 95% of users who won't use multithreading, and get the cost of threads and mutex locking for each OpenGL call they do through SFML ?

In fact... do you have some solid test / reference when you say "decrease performance slightly" ? ;)
Title: [Solved] Loading Images Inside a Thread
Post by: workmad3 on May 03, 2008, 11:11:31 pm
with the simplicity of multi-threading with SFML, do you have any statistics showing it's only 5% that use threading? ;) It's certainly something I'm relying on heavily in the code I'm writing at the moment due to it's simplicity.

As for slightly... I don't have any solid reference data, but the process is a very common pattern, especially with what I do at work where performance is a major concern :)
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 04, 2008, 08:13:44 am
It seems we need a real benchmark ;)

I'll try to implement it and do some tests. I'll let you know the results.
Title: [Solved] Loading Images Inside a Thread
Post by: workmad3 on May 04, 2008, 08:18:48 pm
Sounds good :)

I'll have a go too when I have a chance, see what sort of implementation I can come up with myself ;)
Title: [Solved] Loading Images Inside a Thread
Post by: deps on May 09, 2008, 01:13:40 pm
Just one idea that popped up in my head while reading this.

How about you load the images into the system RAM instead of the graphics memory? (Like in the good old days)
Add a flag to the image loading functions to let people decide for them self if they want to use this.
Then, when the thread is finished you just call a function that transfers it into graphics memory. Would that work? Or do the image loading code depend much on OpenGL?

Forgive me if it was a silly idea, I'm not that skilled when it comes to lowlevel stuff. :)
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 09, 2008, 02:55:50 pm
I first thought it was not a good idea, but then I realized that it could easily be done without altering the public interface, nor requiring extra calls from the user.

I modified the code so that internal OpenGL textures are now not created right after loading, but rather on first use. So that loading can be done anywhere with no restriction.

Thanks for the idea :)
Title: [Solved] Loading Images Inside a Thread
Post by: deps on May 09, 2008, 03:26:42 pm
:shock:

I... I made an contribution? Holy crap!  :lol:

How about a function to create the internal textures right away? Some people might want to know that everything is loaded and ready when the main loop starts. (just to make everyone happy :)
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 09, 2008, 04:35:20 pm
Quote
How about a function to create the internal textures right away? Some people might want to know that everything is loaded and ready when the main loop starts. (just to make everyone happy

There's no point doing that, people just want to know that everything's working as expected and shouldn't care about the internal stuff as long as it has no impact on the external behaviour.
Title: [Solved] Loading Images Inside a Thread
Post by: workmad3 on May 09, 2008, 04:39:51 pm
If you really want to, create an invisible view and draw your images to it right after loading them to force them into OGL :)
Title: [Solved] Loading Images Inside a Thread
Post by: deps on May 09, 2008, 04:49:44 pm
That's one way to do it. :)
I don't think I would need such a function anyway. Was just trying to think ahead a little. :)
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 09, 2008, 05:02:34 pm
Quote from: "workmad3"
If you really want to, create an invisible view and draw your images to it right after loading them to force them into OGL :)

Calling Bind() would be enough ;)
Title: [Solved] Loading Images Inside a Thread
Post by: workmad3 on May 09, 2008, 05:58:08 pm
Quote from: "Laurent"
Quote from: "workmad3"
If you really want to, create an invisible view and draw your images to it right after loading them to force them into OGL :)

Calling Bind() would be enough ;)

Give me a break... I haven't actually looked beyond class names in the 2d api so far :D I may look into it some more if I ever get beyond my action system in SFML ;)
Title: [Solved] Loading Images Inside a Thread
Post by: lzr on May 10, 2008, 08:47:31 am
Awesome, so I should expect everything to work upon the next release of SFML. And to think, I was about to change the loading code. Great work guys, my game is all finished. Hopefully the next version will come out soon so I can put it online. Is the new thread friendly image loading code in the SVN already?
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on May 10, 2008, 10:55:51 am
It's not on SVN yet, I first have to complete another major modification. But it should be done in a few days.

I'm also thinking about releasing version 1.3 soon.
Title: [Solved] Loading Images Inside a Thread
Post by: lzr on June 22, 2008, 10:49:47 am
I didn't see it in the Release log of SFML 1.3. Was this feature left out? I did program my own version that works, but it would great if it was in the official release.
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on June 22, 2008, 11:41:10 am
It's only a little internal optimization which doesn't have any impact on the public interface, that's why it's not in the change log. But it's done, you can now load an image inside a thread ;)
Title: [Solved] Loading Images Inside a Thread
Post by: dewyatt on July 15, 2008, 12:39:39 am
This is a little old but..

I've read all of this but I'm still wondering if it's possible to load images in a thread and draw something indicating progress (like an sf::String with percent).
I think laurent said it isn't really possible.
But is it not possible even with a mutex?

Sorry, I'm new to multi-threading for the most part.
And could anyone provide a tiny bit of sample code for loading images in a thread?
Title: [Solved] Loading Images Inside a Thread
Post by: dewyatt on July 15, 2008, 01:18:16 am
Okay, I answered my own question.
I'll post the code in a sec in case it helps someone else.
Title: [Solved] Loading Images Inside a Thread
Post by: dewyatt on July 15, 2008, 03:21:40 am
Okay, I put it on the wiki.

Here's the source:
http://www.sfml-dev.org/wiki/en/sources/loadimagesinthread.cpp

Here's a little tutorial (sort of):
http://www.sfml-dev.org/wiki/en/tutorials/loadimagesinthread
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on July 15, 2008, 04:53:56 am
Nice, thanks :)
Title: [Solved] Loading Images Inside a Thread
Post by: dewyatt on July 15, 2008, 08:04:22 am
I'm happy to contribute.  :)

Updated the tutorial and code.
Also added a download.
Title: [Solved] Loading Images Inside a Thread
Post by: dabo on July 15, 2008, 11:46:23 am
great addition, thanks.
Title: [Solved] Loading Images Inside a Thread
Post by: opatut on November 07, 2010, 06:33:21 pm
It's time to revive this thread...

I am currently trying to implement the same as mentioned above: a loading screen and a thread loading the images in background. My current problem is a segfault I get when loading an Image (sf::Image::LoadFromFile) from the thread into a boost::ptr_map.

GDB outputs the following:
Code: [Select]
#0  0x00007ffff4d08669 in glGetIntegerv () from /usr/lib/libGL.so.1
#1  0x00007ffff794cd83 in sf::Image::GetMaximumSize() ()
    from /usr/lib/libsfml-graphics.so.2.0
#2  0x00007ffff794ce38 in sf::Image::CreateTexture() ()
    from /usr/lib/libsfml-graphics.so.2.0
#3  0x00007ffff794d544 in sf::Image::LoadFromFile(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()
    from /usr/lib/libsfml-graphics.so.2.0
#4 ...


This seems to be an OpenGL context error.
Did you change anything about loading images in different threads in SFML 2, Laurent? Or is it just my fault?

/edit: I try to do all this without a Mutex as it is not my intention to lock any thread. I just want them to run simultaneously. In the main thread I poll every tick whether the loading thread has finished, so I can continue my program. I do not need the synchronicity provided by a mutex and I want to avoid a deadlock.
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on November 07, 2010, 07:38:32 pm
You should show your code.
Title: [Solved] Loading Images Inside a Thread
Post by: opatut on November 07, 2010, 09:55:19 pm
That's a bit complicated as it is a whole engine I'm working on, but I'll try to pick the important pieces together.

This is how I start the thread:
Code: [Select]
void ResourceManager::LoadAllQueuedImages(void* data) {
auto resmgr = Root::get_mutable_instance().GetResourceManagerPtr();
while(resmgr->GetPercentageLoadingDone() < 1) {
resmgr->LoadNextImage();
}
}

void ResourceManager::StartLoadingAllQueuedImagesInBackground() {
sf::Thread thread(&ResourceManager::LoadAllQueuedImages);
thread.Launch();
sf::Sleep(20.f); // segfault occurs immediately, so it must be caused by the loader thread
}


ResourceManager::LoadNextImage() deques the next image, loads it and saves it into the boost::ptr_map. This is all a bit complicated, as I use ImageMagick to convert from *.svg, but without multithreading it works fine. The important part:

   
Code: [Select]
// Load cached file
sf::Image sfimage;
sfimage.LoadFromFile(cacheFile);
// Save loaded Image in map
mImages[image_key] = sfimage;




In my main loop I call ResourceManager::StartLoadingAllQueuedImagesInBackground() once and get the status from the ResourceManager. As I only read integers, this should be fine. And of course in debug this does not happen, as I have 20 seconds delay to avoid problems between the two threads.
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on November 07, 2010, 10:43:06 pm
So you never use a sf::Context instance?
Title: [Solved] Loading Images Inside a Thread
Post by: opatut on November 07, 2010, 11:24:00 pm
Now I do ;) It has not been mentioned in the 1.6 multithreading tutorial and I did not find anything about it...

And the 1.6 API says:
Quote
It's meant to be used internally.


Anyway, it works now. Thanks!
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on November 07, 2010, 11:54:07 pm
Yeah, I don't even remember how it is supposed to work in SFML 1.6 :lol:

But you were talking about SFML 2, so...
Title: [Solved] Loading Images Inside a Thread
Post by: opatut on November 13, 2010, 10:42:49 pm
I am still not getting the results I want. I added an sf::Context in my loading thread, but after loading all the images they are not available from the main thread. It's not that I can't access the objects. I can even retrieve the width and height, but drawing them does nothing. I think this is again the Open GL context problem. Is it possible to use the same context in both threads or will it crash? Or do I have to load the raw data into RAM and wrap them into the sf::Image from the main thread?

/Edit: Width and Height seem to be always 48, exept while Loading the images, the GUI, which is loaded synchronously has other values. Is 48 x 48 the default size for any sf::Image?
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on November 13, 2010, 11:06:54 pm
It should work, all OpenGL contexts are shared.

Can you show your code?
Title: [Solved] Loading Images Inside a Thread
Post by: opatut on November 14, 2010, 12:05:39 am
I don't even know what I changed over the last few minutes but now it works. Must have been the awareness that it HAS to work like this ;) Thanks, Laurent!
Title: [Solved] Loading Images Inside a Thread
Post by: Svenstaro on November 14, 2010, 06:59:00 pm
I still seem to have problems with this. I'm working on the project with opatut and the threading stuff that works for him crashes for me. I debugged it quite a bit and figured it must be an implementation problem but I do not know who is at fault.

Relevant parts of backtrace on my machine (Arch Linux x86_64, Mobility Radeon X2100 with r500g drivers, mesa from recent git):

Quote
#0  0x0000000000000020 in ?? ()
#1  0x00007fffef33d1af in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#2  0x00007fffef34b882 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#3  0x00007fffef4020b9 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#4  0x00007fffef49af57 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#5  0x00007fffef3e6b21 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#6  0x00007ffff794eccb in sf::Image::EnsureTextureUpdate() const ()
   from /usr/lib/libsfml-graphics.so.2.0
#7  0x00007ffff794ecf9 in sf::Image::Bind() const ()
   from /usr/lib/libsfml-graphics.so.2.0
#8  0x00007ffff7964a3d in sf::Renderer::SetTexture(sf::Image const*) ()
   from /usr/lib/libsfml-graphics.so.2.0
#9  0x00007ffff7969132 in sf::Sprite::Render(sf::RenderTarget&, sf::Renderer&) const ()
   from /usr/lib/libsfml-graphics.so.2.0
#10 0x00007ffff796581a in sf::RenderTarget::Draw(sf::Drawable const&) ()
   from /usr/lib/libsfml-graphics.so.2.0
#11 0x000000000055b93d in Submarine::Draw (this=0x7fffe4037390, target=0x8410a8)
    at /home/svenstaro/NoisyHunter/game/src/common/Submarine.cpp:111


Surprisingly, it works when I start it in valgrind (albeit with corruption). I originally suspected a racing condition in our code. However, it works just fine for the nvidia users with proprietary nvidia drivers.

The thread is launched as follows:
Code: [Select]
void ResourceManager::LoadAllQueuedImages(void* data) {
sf::Context context;
auto resmgr = Root::get_mutable_instance().GetResourceManagerPtr();
while(!resmgr->GetLoadingStatus().IsFinished()) {
resmgr->LoadNextImage();
}
}

The problematic line here is sf::Conext context. If I take it out, the thread breaks for nvidia users due to missing glContext but works for me (though I get white images as expected). If I put it in, nvidia users observe expected behavior but it crashes for me with the above backtrace unless I run it in valgrind, oddly enough.

The problem isn't even that the sf::Context can't somehow be created. For instance, if I run it with
Code: [Select]
void ResourceManager::LoadAllQueuedImages(void* data) {
sf::Context context;
auto resmgr = Root::get_mutable_instance().GetResourceManagerPtr();
while(!resmgr->GetLoadingStatus().IsFinished()) {
resmgr->LoadNextImage();
}
        sf::Sleep(10.f);
}

I can play for 10 seconds until the thread goes out of scope. The behavior is just as expected in that case. I'd idle the thread indefinitely but that looks like an ugly hack. I tried creating a memory leak like this:
Code: [Select]
void ResourceManager::LoadAllQueuedImages(void* data) {
sf::Context* context = new sf::Context();
auto resmgr = Root::get_mutable_instance().GetResourceManagerPtr();
while(!resmgr->GetLoadingStatus().IsFinished()) {
resmgr->LoadNextImage();
}
}

and it works without crash. It gives me almost the expected behavior. I get a few image corruptions in the images loaded in the thread. Not all images come out corrupt, but those that do are so corrupt that my driver refuses rendering them (I get a kernel exception for an invalid texture steam).

I tested the exactly same stuff on a Arch Linux i686-based Radeon Mobility FireGl 9000-series card with r200 (non-gallium) drivers and the behavior is almost exactly the same as on my r500g (the corruption looks different).

Suspecting a problem in the mesa part of this, I ran it on my Arch Linux x86_64 Virtualbox guest. In this case, the sf::Context never was created and the images were never actually loaded. They all came out white. The Virtualbox guest uses their accelerated Chromium drivers.

Can you give us some insight here? At first glance it would seem as though the drivers are at fault but on a second look this might not be actually true and the sf::Context might in fact be the problem. Any help appreciated.
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on November 14, 2010, 07:19:35 pm
It's hard to tell from such a little piece of code. If you could write a complete and minimal example, I could test it and debug it on my computer.
Title: [Solved] Loading Images Inside a Thread
Post by: Svenstaro on November 14, 2010, 08:00:29 pm
Le minimal example:

Code: [Select]
#include <SFML/Graphics.hpp>

sf::Image image;
bool done = false;

void Thread(void* data) {
    // Swapping this causes corruption but works (kinda)
    //Sf::Context* c = new sf::Context();
    sf::Context c;
   
    // http://paradoxdgn.com/junk/avatars/trollface.jpg
    image.LoadFromFile("trollface.jpg");
    done = true;
}

int main() {
    // Create main window
    sf::RenderWindow app(sf::VideoMode(800,600,32), "Test window");

    // Load image in thread
    sf::Thread t(&Thread);
    t.Launch();

    while(app.IsOpened()) {
        sf::Event Event;
        while(app.GetEvent(Event)) {
            if(Event.Type == sf::Event::Closed) {
                app.Close();
            }
        }      
       
        app.Clear(sf::Color::White);
        // show image as soon as it is loaded
        if(done) {
            sf::Sprite lol(image);
            lol.SetPosition(100,100);
            app.Draw(lol);
        }

        app.Display();
        sf::Sleep(1);
    }
}


Gives us:
Code: [Select]
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff25b0f1a in radeon_bo_ref () from /usr/lib/libdrm_radeon.so.1
(gdb) bt
#0  0x00007ffff25b0f1a in radeon_bo_ref () from /usr/lib/libdrm_radeon.so.1
#1  0x00007ffff25b0db8 in radeon_cs_space_add_persistent_bo ()
   from /usr/lib/libdrm_radeon.so.1
#2  0x00007ffff2a2692c in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#3  0x00007ffff2a28810 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#4  0x00007ffff2a28a38 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#5  0x00007ffff2bb5482 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#6  0x00007ffff2a3ace7 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#7  0x00007ffff2a219dc in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#8  0x00007ffff2b71050 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#9  0x00007ffff2abcb21 in ?? () from /usr/lib/xorg/modules/dri/r300_dri.so
#10 0x00007ffff7b5eccb in sf::Image::EnsureTextureUpdate() const ()
   from /usr/lib/libsfml-graphics.so.2.0
#11 0x00007ffff7b5ecf9 in sf::Image::Bind() const ()
   from /usr/lib/libsfml-graphics.so.2.0
#12 0x00007ffff7b74a3d in sf::Renderer::SetTexture(sf::Image const*) ()
   from /usr/lib/libsfml-graphics.so.2.0
#13 0x00007ffff7b79132 in sf::Sprite::Render(sf::RenderTarget&, sf::Renderer&) const
    () from /usr/lib/libsfml-graphics.so.2.0
#14 0x00007ffff7b7581a in sf::RenderTarget::Draw(sf::Drawable const&) ()
---Type <return> to continue, or q <return> to quit---
   from /usr/lib/libsfml-graphics.so.2.0
#15 0x0000000000401949 in main () at main.cpp:34
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on November 14, 2010, 08:28:05 pm
With which revision of SFML 2?
Title: [Solved] Loading Images Inside a Thread
Post by: Svenstaro on November 14, 2010, 08:52:41 pm
Newest as of right now: 1639 (on all tested computers).
Title: [Solved] Loading Images Inside a Thread
Post by: opatut on November 18, 2010, 08:42:34 pm
bump
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on November 18, 2010, 09:33:32 pm
Yeah, don't worry, it's on my todo-list ;)
Title: [Solved] Loading Images Inside a Thread
Post by: opatut on February 06, 2011, 10:05:31 pm
Bump. What happened to this? Could you reconstruct the error on your machine? We'd be glad to see a fix for our loading screen ;)
Title: [Solved] Loading Images Inside a Thread
Post by: Laurent on February 06, 2011, 10:45:20 pm
I'm working on fixing the ATI bug, which could also solve your problem.
Title: Re: [Solved] Loading Images Inside a Thread
Post by: SajSFML on December 26, 2021, 09:10:14 pm
Is there any update on this? I also want to load images in separate threads, but I found this topic and it seems to be abandoned.