SFML community forums
Help => Graphics => Topic started by: T.T.H. on July 12, 2010, 03:54:07 pm
-
I've ran into the following problem: when a render image is created in one thread it cannot be modified anymore in another thread. The render image is still there and it can e.x. be saved, but whatever modification (clear, draw, etc.) I throw at it the image won't change. Please note that I already do allocate an sf::Context in every thread.
The following minimalistic code example demonstrates the problem:
// include SFML
#include <SFML/Graphics.hpp>
// include STD
#include <iostream>
#include <fstream>
#include <sstream>
// include threads
#include <windows.h>
#include <process.h>
// global variable (for simplicity)
sf::RenderImage* g_pRenderImage = NULL;
// thread function
unsigned int _stdcall ThreadFunction(void* pParam)
{
// create SFML context in every thread
sf::Context MyContext;
// get and output thread index
int* pThreadIndex = (int*) pParam;
std::cout << "thread " << *pThreadIndex << " started" << std::endl;
// create or reuse render image
if (g_pRenderImage == NULL) {
std::cout << " creating render image" << std::endl;
g_pRenderImage = new sf::RenderImage;
g_pRenderImage->Create(300, 100);
} else {
std::cout << " reusing render image" << std::endl;
}
// loop
int ImageIndex;
for (ImageIndex = 0; ImageIndex < 2; ++ImageIndex) {
// clear render image
g_pRenderImage->Clear(sf::Color(200, 200, 200));
// create label
std::stringstream Label;
Label << "thread" << *pThreadIndex << "image" << ImageIndex;
// create SFML text from label
sf::Text MyText(Label.str());
MyText.SetColor(sf::Color(255, 0, 0));
MyText.SetPosition(20.0f, 20.0f);
// draw SFML text
g_pRenderImage->Draw(MyText);
// display (finish) render image
g_pRenderImage->Display();
// save render image
std::cout << " saving image '" << Label.str() << ".png'" << std::endl;
g_pRenderImage->GetImage().SaveToFile(Label.str() + ".png");
}
std::cout << " done" << std::endl;
return 0;
}
// main function
int main(int argc, char** argv)
{
// create SFML context in main thread
sf::Context MyContext;
// loop over threads
int ThreadIndex;
for (ThreadIndex = 0; ThreadIndex < 2; ++ThreadIndex) {
// create thread
unsigned int TempThreadID = 0;
HANDLE TempThreadHandle = (HANDLE) _beginthreadex(
NULL, // no security attributes
0, // use default stack size
ThreadFunction, // thread function
&ThreadIndex, // argument to thread function
0, // use default creation flags
&TempThreadID); // returns the thread identifier
// handle thread creation error
if (TempThreadHandle == NULL) {
std::cout << "failed to create thread " << ThreadIndex << std::endl;
return 1;
}
// wait till thread finishes
WaitForSingleObject(TempThreadHandle, INFINITE);
}
return 0;
}
This code was made with Visual C++ 2003 .NET on Windows XP 32 Bit using https://sfml.svn.sourceforge.net/svnroot/sfml/branches/sfml2 revision 1523.
After running this code there will be 4 image files:
"thread0image0.png" containing the text "thread0image0" which is correct.
"thread0image1.png" containing the text "thread0image1" which is correct.
"thread1image0.png" containing the text "thread0image1" which is wrong.
"thread1image1.png" containing the text "thread0image1" which is wrong.
Based on my actual, big project using multiple threads seems to work for render windows, but as the example above shows it does not work for render images.
I'd like to know whether I'm doing something wrong, whether this is intended/expected behaviour or whether this is a bug, thanks.
-
Render-images have their own context, it must explicitely be deactivated before being used in another thread.
g_pRenderImage->SetActive(false);
Note that you don't need to do anything for activating the context, it is always done implicitely.
-
Thanks for the hint, but where exactly in my code do I have to put this statement? (sorry, can't get it to work myself)
-
Forget about your example, it won't work anyway. You would have to use a mutex (at least) to make sure that a thread can complete a frame without being interrupted by another one.
You have to call this function when you're done using a render-image in one thread, and before another thread starts using it.
-
Forget about your example, it won't work anyway. You would have to use a mutex (at least) to make sure that a thread can complete a frame without being interrupted by another one.
In my example is a...
WaitForSingleObject(TempThreadHandle, INFINITE);
...which ensures that no thread runs in parallel.
In my actual project it's a bit different but even there I already ensure that the very same thread renders a complete frame.
You have to call this function when you're done using a render-image in one thread, and before another thread starts using it.
This would be here then...
<snip>
// display (finish) render image
g_pRenderImage->Display();
// save render image
std::cout << " saving image '" << Label.str() << ".png'" << std::endl;
g_pRenderImage->GetImage().SaveToFile(Label.str() + ".png");
}
// fix?
g_pRenderImage->SetActive(false); // <<<<<< NEW <<<<<<<
std::cout << " done" << std::endl;
return 0;
}
// main function
int main(int argc, char** argv)
{
<snip>
...but this does not fix the issue.
-
which ensures that no thread runs in parallel.
Sorry, I read your code too quickly, and I thought it was a WaitForMultipleObject outside the loop :)
but this does not fix the issue.
Indeed, there's something wrong here. I'll do more tests and I'll let you know if I can find something.
-
It seems to be related to the FBO implementation of render-images. So if you need to make it work until I find a solution, you can disable the corresponding code in src/SFML/Graphics/RenderImage.cpp:
/* ----- commented ---- if (priv::RenderImageImplFBO::IsSupported())
{
// Use FBO
myRenderImage = new priv::RenderImageImplFBO;
}
else ----- end of comment ----- */ if (priv::RenderImageImplPBuffer::IsSupported())
{
// Use P-Buffer
myRenderImage = new priv::RenderImageImplPBuffer;
}
-
Thank you, that change in RenderImages.cpp does solve the issue.
It is even not necessary to call...
g_pRenderImage->SetActive(false);
...then.
-
It is even not necessary to call...
Ah, you're right, this is not necessary for render images. It's only required for render windows.