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

Author Topic: How to load images in a dedicated thread?  (Read 5005 times)

0 Members and 1 Guest are viewing this topic.

MickeyKnox

  • Jr. Member
  • **
  • Posts: 53
    • View Profile
How to load images in a dedicated thread?
« on: December 30, 2013, 01:35:42 am »
Hi, i'm writing an application where i want to load images throughout the hole time,
the application runs. Loading images however slows the application noticebly for a moment.
Therefore, i want to load the images in a dedicated thread. But i'm having trouble to figure
out the best approach to do that.

Just to clarify:
I'm not  having trouble with the Thread data type and C++ programming,
but i'm seeking help with my code design.

Thanks~

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: How to load images in a dedicated thread?
« Reply #1 on: December 30, 2013, 01:54:29 am »
Just to clarify:
I'm not  having trouble with the Thread data type and C++ programming,
but i'm seeking help with my code design.
That doesn't really clarify a lot; can you be more concrete? Some general suggestions: Use the C++11 threading library if available, not sf::Thread. Reduce synchronization to a minimum. And if you use a flag to check if the resource has already been loaded, make sure it's atomic.

Just to make that clear, threads won't help you if the bottleneck is the I/O from the hard drive. So, don't try to play a video by displaying single images. What threads can do is keeping the GUI or game responsive while resources are loaded in the background.

By the way, line breaks occur automatically, manual ones only make the text more difficult to read ;)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

MickeyKnox

  • Jr. Member
  • **
  • Posts: 53
    • View Profile
Re: How to load images in a dedicated thread?
« Reply #2 on: December 30, 2013, 02:43:51 am »
Why not use sf::Thread?

I'm displaying one image after another, with a few seconds in between; enough time to load the next image. If i do that sequentially there is a small but noticeable lag in the animation of the image. Therefore i would like to do that in another thread. Now what would be a good design for that?

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: How to load images in a dedicated thread?
« Reply #3 on: December 30, 2013, 03:15:19 pm »
Why not use sf::Thread?
You can use it, but the standard library provides far more functionality (synchronization mechanisms, atomics, futures, ...). Using std::thread also has the advantage that other developers will understand your code. But for your simple task, it doesn't matter too much, and the porting effort is very small anyway.

Now what would be a good design for that?
You could start a thread for each image, that exists only as long as an image is loaded, or have a persistent one that idles while there is no image to load. You need to transfer the loaded image to the main thread, but in this case you don't need concurrent access: As soon as the image is loaded, the loading thread doesn't need to touch it anymore. Therefore the main thread can access it without a mutex (but it needs to check a flag telling whether the loading has finished). A simple option to transport the image to the main thread is to use an output parameter sf::Image& in the thread function, you can then bind it with std::ref() (and std::bind() for SFML threads).

If you have a good understanding of the C++11 threading library, you can also use the high-level operation std::async() which returns a std::future.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

MickeyKnox

  • Jr. Member
  • **
  • Posts: 53
    • View Profile
Re: How to load images in a dedicated thread?
« Reply #4 on: December 30, 2013, 03:47:41 pm »
Thank you for your explanations so far.

Quote
... the porting effort is very small anyway.

Why is there a porting effort? I would've assumened, that both sf::Thread as well as std::thread is platform independent.

I tried firering a thread each time i load an image, but i think that the thread creation overhead is too big (there was still a noticeable lag). My other approach was to create the thread only once and to call launch each time i want to load an image. That however didn't work. Maybe i shouldn't call launch more than once?

Anyway, i'm not creating an sf::Image, but a sf::Texture. My understanding is that not only reading the image from disk, but also copying it to the graphics memory is a heavy operation. I'm describing this because of the weird errors i got. Is it possible, that i'm not allowed to access the graphics card from two different threads?

Quote
If you have a good understanding of the C++11 threading library, you can also use the high-level operation std::async() which returns a std::future.

That sounds very interesting, i will look into that.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: How to load images in a dedicated thread?
« Reply #5 on: December 30, 2013, 04:00:06 pm »
Why is there a porting effort? I would've assumened, that both sf::Thread as well as std::thread is platform independent.
I meant porting sf::Thread to std::thread.

I tried firering a thread each time i load an image, but i think that the thread creation overhead is too big (there was still a noticeable lag).
Don't think, know. 8)

Measure what caused the overhead (using a profiler or at least SFML clocks) and make sure you're not making any other mistakes.

My other approach was to create the thread only once and to call launch each time i want to load an image.
It's launch() that creates the thread. The sf::Thread class is just a handle for it, its lifetime is not the same as the thread's.

Anyway, i'm not creating an sf::Image, but a sf::Texture. My understanding is that not only reading the image from disk, but also copying it to the graphics memory is a heavy operation. I'm describing this because of the weird errors i got. Is it possible, that i'm not allowed to access the graphics card from two different threads?
I'm not very experienced with graphics-related operations in multiple threads, but it might require an OpenGL context. But I'm not sure if it works well at all, maybe someone else can answer here.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

MickeyKnox

  • Jr. Member
  • **
  • Posts: 53
    • View Profile
Re: How to load images in a dedicated thread?
« Reply #6 on: January 01, 2014, 05:22:20 pm »
I've implemented a class to load textures asynchronously. But that resulted in the following error:
Code: [Select]
[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
imani: xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.
Aborted (core dumped)

Thus, to load textures asynchronously, i'de need to call XInitThreads() first. However, if i'm getting Laurents comment on this thread (http://en.sfml-dev.org/forums/index.php?topic=3138.0) right, this solution wouldn't be portable.

I therefore decided to only load the Images asynchronously and creating a texture for them in the main program. That did the trick. The overhead to load the images to the graphics card memory seems to be negligible.

In case anybody would like to see a working example, here comes my implementation:
Code: [Select]
ImageLoader.h

#include <future>
#include <SFML/Graphics.hpp>

class ImageLoader
{
    private:
        sf::Image *image;
        std::future<int> future;
        std::vector<std::string> pictureNames;

        int parallelMethod(void);

    public:
        ImageLoader(const std::string&);
        void launch(void);
        sf::Image* getImage(void);
};

ImageLoader.cc:

#include "ImageLoader.h"
#include "FileHelper.h"

ImageLoader::ImageLoader(const std::string &dir)
{
    pictureNames = FileHelper::loadFilenames(dir);
}

int
ImageLoader::parallelMethod()
{
    image = new sf::Image();
    while (image->loadFromFile(pictureNames[rand() % pictureNames.size()]) == false);
    return 0;
}

void
ImageLoader::launch()
{
    future = std::async(std::launch::async, &ImageLoader::parallelMethod, this);
}

sf::Image*
ImageLoader::getImage()
{
    future.wait();
    return image;
}

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: How to load images in a dedicated thread?
« Reply #7 on: January 01, 2014, 09:16:02 pm »
Why does the future hold an int? The idea of std::async() is that you create straightforward functions that can be easily parallelized, without the need for explicit synchronization. The future is supposed to return a resource being computed/loaded, not a useless integer.

The way you use futures doesn't make sense, you could as well work with threads instead (call std::thread::join() for blocking waits). Don't use futures if you don't understand threads -- these are high-level features that require knowledge of the underlying concepts in order to be used effectively. As stated in a previous post, you need a good understanding of the C++11 threading library...

I recommend you go with std::thread or sf::Thread. The implementation won't be complicated either, and you'll get experience with multithreading that can help you use more advanced classes like std::future later.
« Last Edit: January 01, 2014, 09:18:23 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development: