Hi,
I've been trying to run my game engine doing all the rendering in a different thread following the tutorial
http://www.sfml-dev.org/tutorials/2.2/graphics-draw.php#drawing-from-threadsI've also read
http://www.sfml-dev.org/tutorials/2.2/window-window.php#things-to-know-about-windows and if I understood correctly, in Mac there are these two main constraints:
- Events must be polled in the window's thread
- On OS X, windows and events must be managed in the main thread --> Mac OS X just won't agree if you try to create a window or handle events in a thread other than the main one.
I get I can't neither poll the events nor create the window in a different thread than the main one. What is happening to me, though, is that the moment I render something, on destruction of the window I have the following crash:
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Application Specific Information:
Assertion failed: (localPool != NULL), function releasePool, file /Users/pablo/Game/ThirdParty/SFML/src/SFML/Window/OSX/AutoreleasePoolWrapper.mm, line 203.
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libsystem_kernel.dylib 0x00007fff833f5282 __pthread_kill + 10
1 libsystem_c.dylib 0x00007fff8d081b73 abort + 129
2 libsystem_c.dylib 0x00007fff8d049c59 __assert_rtn + 321
3 libsfml-window-d.2.2.0.dylib 0x000000010c231f81 releasePool() + 305
4 libsfml-window-d.2.2.0.dylib 0x000000010c2285d2 sf::priv::SFContext::~SFContext() + 146
5 libsfml-window-d.2.2.0.dylib 0x000000010c228635 sf::priv::SFContext::~SFContext() + 21
6 libsfml-window-d.2.2.0.dylib 0x000000010c228658 sf::priv::SFContext::~SFContext() + 24
7 libsfml-window-d.2.2.0.dylib 0x000000010c206e95 sf::priv::GlContext::globalCleanup() + 693
8 libsfml-window-d.2.2.0.dylib 0x000000010c20ad66 sf::GlResource::~GlResource() + 70
9 libsfml-window-d.2.2.0.dylib 0x000000010c211ba3 sf::Window::~Window() + 51
10 libsfml-graphics-d.2.2.0.dylib 0x000000010c289aed sf::RenderWindow::~RenderWindow() + 45
11 libsfml-graphics-d.2.2.0.dylib 0x000000010c289b45 sf::RenderWindow::~RenderWindow() + 21
12 Game 0x000000010c18de47 main + 679
13 libdyld.dylib 0x00007fff87f7e5c9 start + 1
This is a minimal example to reproduce the issue:
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
bool exitThread = false;
void renderingThread(sf::RenderWindow* window)
{
printf("Rendering thread %lu started\n", std::hash<std::thread::id>()(std::this_thread::get_id()));
sf::CircleShape shape(50);
shape.setFillColor(sf::Color(150, 50, 250));
// set a 10-pixel wide orange outline
shape.setOutlineThickness(10);
shape.setOutlineColor(sf::Color(250, 150, 100));
// the rendering loop
while (!exitThread && window->isOpen())
{
// draw...
window->draw(shape); // if I comment this line, it works
// end the current frame
window->display();
}
}
int main(int argc, char* argv[]) {
printf("Main thread %lu started\n", std::hash<std::thread::id>()(std::this_thread::get_id()));
//create the window (remember: it's safer to create it in the main thread due to OS limitations)
sf::RenderWindow window(sf::VideoMode(800, 600), "OpenGL");
// deactivate its OpenGL context
window.setActive(false);
// launch the rendering thread
sf::Thread thread(&renderingThread, &window);
thread.launch();
// the event/logic/whatever loop
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed || (event.key.code == sf::Keyboard::Escape)) {
printf("Closing\n");
// closing the window when renderingThread still uses it causes lots of
// GL_INVALID_FRAMEBUFFER_OPERATION, the object bound to FRAMEBUFFER_BINDING is not "framebuffer complete"
//window.close();
exitThread = true;
thread.wait();
window.close();
}
}
}
return 0;
}
If I don't draw anything, it doesn't crash. I changed slightly the AutoreleasePoolWrapper, SFContext and WindowImplCocoa to check what it was retaining and releasing from which thread adding a few log traces:
This is the output of the code provided at the end in debug:
Main thread 4217047789276772314 started
SFContext::SFContext 0x7fa603c1db30 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 1 time from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: creating new pool from thread 4217047789276772314
SFContext::SFContext 0x7fa603e0a440 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 2 time from thread 4217047789276772314
WindowImplCocoa::VideoMode 0x7fa603e0c230 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 3 time from thread 4217047789276772314
SFContext::SFContext,WindowImpl 0x7fa603d0c680 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 4 time from thread 4217047789276772314
Rendering thread 13536384218981027915 started
SFContext::SFContext 0x7fa6058649d0 retainPool from thread 13536384218981027915
AutoreleasePoolWrapper::retainPool: entered for 5 time from thread 13536384218981027915
AutoreleasePoolWrapper::retainPool: creating new pool from thread 13536384218981027915
SFContext::~SFContext 0x7fa6058649d0 releasePool from thread 13536384218981027915
AutoreleasePoolWrapper::releasePool: entered for 1 time from thread 13536384218981027915
AutoreleasePoolWrapper::releasePool: releasing pool for thread 13536384218981027915
SFContext::SFContext 0x7fa605859e80 retainPool from thread 13536384218981027915AutoreleasePoolWrapper::retainPool: entered for 6 time from thread 13536384218981027915
AutoreleasePoolWrapper::retainPool: creating new pool from thread 13536384218981027915
Closing
SFContext::~SFContext 0x7fa603d0c680 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 2 time from thread 4217047789276772314
WindowImplCocoa::~WindowImplCocoa 0x7fa603e0c230 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 3 time from thread 4217047789276772314
SFContext::~SFContext 0x7fa603c1db30 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 4 time from thread 4217047789276772314
SFContext::~SFContext 0x7fa603e0a440 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 5 time from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: releasing pool for thread 4217047789276772314
SFContext::~SFContext 0x7fa605859e80 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 6 time from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: has NULL localPool for thread 4217047789276772314. assert expectedAssertion failed: (localPool != NULL), function releasePool, file /Users/pablo/Game/ThirdParty/SFML/src/SFML/Window/OSX/AutoreleasePoolWrapper.mm, line 208.
Abort trap: 6
It seems pretty clear that since AutoreleasePoolWrapper is using localPool as a TLS variable, and the SFContext from the rendering thread is called from that thread whereas its deconstructor is called from the main one. When this happens, localPool from the main thread has already been released. The question I guess is whether I'm doing something wrong or this is the usual behavior and I'm missing something in here. Please feel free to give any feedback cause I've already tried tons of things.
Something as simple as adding a checking would "solve" the crash, but it doesn't solve the deep issue which is that the constructor is called from one thread whereas the desctructor is called from another one, making that the pool of "the other thread" (aka. rendering thread) is never released.
void releasePool(void)
{
if (localPool == NULL) {
return;
}
...
My OS is Yosemite 10.10.1 and I'm using SFML 2.2 compiled by myself.
Sorry for the long post but I wanted to provide as much information as possible to help figuring out the problem. Also, I'm not sure if this is the proper place for this post or Help->Window is better. Please feel free to move it.
Thanks a million in advance