SFML community forums
Help => Graphics => Topic started 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?
-
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.
-
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.
-
Hmm... and how would you do that ?
-
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.
-
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.
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.
-
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 :)
-
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" ? ;)
-
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 :)
-
It seems we need a real benchmark ;)
I'll try to implement it and do some tests. I'll let you know the results.
-
Sounds good :)
I'll have a go too when I have a chance, see what sort of implementation I can come up with myself ;)
-
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. :)
-
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 :)
-
: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 :)
-
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.
-
If you really want to, create an invisible view and draw your images to it right after loading them to force them into OGL :)
-
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. :)
-
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 ;)
-
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 ;)
-
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?
-
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.
-
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.
-
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 ;)
-
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?
-
Okay, I answered my own question.
I'll post the code in a sec in case it helps someone else.
-
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
-
Nice, thanks :)
-
I'm happy to contribute. :)
Updated the tutorial and code.
Also added a download.
-
great addition, thanks.
-
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:
#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.
-
You should show your code.
-
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:
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:
// 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.
-
So you never use a sf::Context instance?
-
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:
It's meant to be used internally.
Anyway, it works now. Thanks!
-
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...
-
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?
-
It should work, all OpenGL contexts are shared.
Can you show your code?
-
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!
-
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):
#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:
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
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:
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.
-
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.
-
Le minimal example:
#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:
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
-
With which revision of SFML 2?
-
Newest as of right now: 1639 (on all tested computers).
-
bump
-
Yeah, don't worry, it's on my todo-list ;)
-
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 ;)
-
I'm working on fixing the ATI bug, which could also solve your problem.
-
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.