Here is the "fixed" code with a workaround (only really necessary in this example):
#include <SFML/Graphics.hpp>
#include <thread>
bool created;
void createTexture(sf::RenderTexture& rt, sf::Sprite& sprite) {
rt.create(512, 480);
sprite.setTexture(rt.getTexture(), true);
created = true;
sf::Context context;
}
int main()
{
sf::RenderWindow window(sf::VideoMode(512, 480), "Test");
window.setFramerateLimit(60);
sf::Texture::getMaximumSize();
sf::Shader::isAvailable();
sf::RenderTexture rt;
sf::Sprite sprite;
created = false;
// just a triangle to draw for tests
sf::VertexArray triangle(sf::Triangles, 3);
triangle[0].position = sf::Vector2f(10, 10);
triangle[1].position = sf::Vector2f(100, 10);
triangle[2].position = sf::Vector2f(100, 100);
triangle[0].color = sf::Color::Red;
triangle[1].color = sf::Color::Blue;
triangle[2].color = sf::Color::Green;
std::thread loadingThread(&createTexture, std::ref(rt), std::ref(sprite));
loadingThread.detach();
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
if (created) {
// the createTexture function is finished by this point
rt.clear();
rt.draw(triangle);
rt.display();
window.draw(sprite);
}
window.display();
}
}
Like dabbertorres said, when you .create() the RenderTexture in a separate thread, it will create its own context there as well. However, unlike what he said, the context doesn't get destroyed in the thread, it gets stored in the RenderTexture object to be activated wherever it is used in the future. By creating the RenderTexture in the secondary thread, you are activating it there, but it will never be deactivated. Deactivation has to be explicit (which isn't what SFML currently does) and does not automatically follow from the termination of a thread. Trying to activate the context for drawing to the RenderTexture in the primary thread will always "fail" (silently) if it is still currently active in another thread, even if that thread is no longer running. The easiest way to deactivate a context in SFML currently is simply to create another temporary one. This will override the active context within a thread.
The sf::Texture::getMaximumSize(); and sf::Shader::isAvailable(); workarounds are really only necessary in this example since you don't make any use of the RenderWindow for drawing before actually creating the RenderTexture. In any bigger application, they would typically be indirectly called from any .draw(), texture or shader call.
OK... so now that that mysterious issue is solved, time for the other thing...
You shouldn't be doing this in the first place.
Maybe this has not happened to you (yet), but continuously creating your RenderTextures in a separate thread will, as described above, also continuously create more and more new contexts which, in the current SFML implementation, unfortunately will not be destroyed until the application exits. You can try doing what you do in this code a few thousand times, it will eventually crash if you are lucky.
Also, I hope you are aware that moving this code into a thread makes little to no sense. If you time the calls that are placed in the thread, you will notice that they take almost no time (ignoring the overhead of constant context creation which is a problem on its own anyway). For our intents and purposes, all OpenGL calls are asynchronous.
- Creating OpenGL resources from a thread does not make sense.
- Drawing from a thread does not make sense.
- Drawing simultaneously from multiple threads definitely does not make sense.
I've probably already said too many times: you only have a single logical GPU. OpenGL only supports the concept of a single command stream. Trying to multitask only wastes your time unless you are really really into advanced OpenGL and understand everything that goes on on the hardware and in the driver. This does not apply to 99% of SFML users, which is the reason I give this general advice.
If you are trying to asynchronously load resources in a secondary thread, then do that, but no more. I often see people get lazy and just throw everything into a thread (even without proper synchronization), which ends up causing even more headaches. The bottleneck is the disk read and loading process, so that is the only thing that has to go into the thread. Everything else can stay in the main thread. If you are loading textures, load the sf::Image in the thread and use the loaded image data in your primary thread once it is ready. Clean multi-threaded resource loading can be done easily in SFML because it is separated out cleanly in the API, people only have to make use of it as well.