*System specification are as follow...
macOS Mojave -> Mac OS X 10.14.2
wxWidgets 3.1.2 -> --with-osx_cocoa --with-macosx-version-min=10.14 --with-macosx=MacOSX10.14.sdk
SFML 2.5.1 -> macOS OS X 10.7+, compatible with C++11 and libc++
The following code snippet, shown below, comes from a GitHub project that you can find at this link https://gist.github.com/eriknelson/e4ccf32534eb3d25a1ea. Please give Eriknelson credit for his SFML 2.3+ into wxWidgets 3.0 integration demo. This demo is not prefect but it only has one flaw. The application crashes after closing the wxWidgets window.
#include <iostream>
#include <wx/wx.h>
#include <memory>
#include <SFML/Graphics.hpp>
#ifdef __WXGTK__
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#endif
using namespace std;
static const int kDefaultWindowWidth = 1280;
static const int kDefaultWindowHeight = 720;
static const int kCanvasMargin = 0;
struct wxSfmlCanvas : public wxControl, public sf::RenderWindow
{
wxSfmlCanvas(
wxWindow *parent = nullptr,
wxWindowID windowId = -1,
const wxPoint &position = wxDefaultPosition,
const wxSize &size = wxDefaultSize,
long style = 0) :
wxControl(parent, windowId, position, size, style)
{
createRenderWindow();
}
virtual void onUpdate(){};
void onIdle(wxIdleEvent& event)
{
// Send a paint message when control is idle, to ensure max framerate
Refresh();
}
void onPaint(wxPaintEvent& event)
{
wxPaintDC dc(this); // Prepare control to be repainted
onUpdate(); // Tick update
display(); // Draw
}
// Explicitly overriding prevents wxWidgets from drawing, which could result in flicker
void onEraseBackground(wxEraseEvent& event){}
void createRenderWindow()
{
#ifdef __WXGTK__
// gtk_widget_realize(m_wxwindow);
// gtk_widget_set_double_buffered(m_wxwindow, false);
// GdkWindow *gdkWindow = gtk_widget_get_window((GtkWidget*)GetHandle());
// XFlush(GDK_WINDOW_XDISPLAY(gdkWindow));
// sf::RenderWindow::create(GDK_WINDOW_XWINDOW(gdkWindow));
#else
sf::RenderWindow::create(GetHandle());
#endif
}
void setwxWindowSize(const wxSize& size)
{
this->SetSize(size);
}
void setRenderWindowSize(const sf::Vector2u& size)
{
this->setSize(size);
}
virtual ~wxSfmlCanvas(){};
wxDECLARE_EVENT_TABLE();
};
struct Canvas : public wxSfmlCanvas
{
Canvas(
wxWindow* parent,
wxWindowID id,
wxPoint position,
wxSize size,
long style = 0) :
wxSfmlCanvas(parent, id, position, size, style)
{
}
virtual void onUpdate()
{
clear(sf::Color::Yellow);
sf::CircleShape shape(50);
shape.setFillColor(sf::Color(150, 50, 250));
shape.setOutlineThickness(10);
shape.setOutlineColor(sf::Color(250, 150, 100));
sf::RectangleShape line(sf::Vector2f(150, 5));
line.rotate(45);
this->draw(shape);
this->draw(line);
}
void onResize(wxSizeEvent& event)
{
auto size = event.GetSize();
auto newCanvasWidth = size.x - (2 * kCanvasMargin);
auto newCanvasHeight = size.y - (2 * kCanvasMargin);
sf::Vector2u v(newCanvasWidth,newCanvasHeight);
wxSize s(newCanvasWidth,newCanvasHeight);
this->SetSize(s); //wx setSize
this->setSize(v); //SFML setSize
}
};
struct AppFrame : public wxFrame
{
AppFrame(const wxString& title, const wxPoint& pos, const wxSize& size) :
wxFrame(NULL, wxID_ANY, title, pos, size),
_panel(new wxPanel(this)),
_canvas(new Canvas(
_panel.get(),
wxID_ANY,
wxPoint(kCanvasMargin, kCanvasMargin),
wxSize(kDefaultWindowWidth - (2 * kCanvasMargin), kDefaultWindowHeight - (2 * kCanvasMargin))
))
{
_panel->SetBackgroundColour(*wxCYAN);
////////////////////////////////////////////////////////////////////////////////
// Probably due to some RTTI, IDE is getting confused by this dynamic call
// and doesn't understand the correct Bind overload. Causes non sequitur errors
// to display in the inspector. Suppress.
//
// Dynamic binding is cleanest here, since we don't want to hook up our resize
// handler until our dependencies (Canvas namely) have finished their initialization
////////////////////////////////////////////////////////////////////////////////
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wint-conversion"
Bind(wxEVT_SIZE, &AppFrame::onResize, this);
#pragma clang diagnostic pop
////////////////////////////////////////////////////////////////////////////////
}
void onResize(wxSizeEvent& event)
{
_canvas->onResize(event);
event.Skip();
}
unique_ptr<wxPanel> _panel;
unique_ptr<Canvas> _canvas;
};
struct App : public wxApp
{
virtual bool OnInit()
{
auto frame = new AppFrame("SFML Canvas Demo", wxPoint(100, 100),
wxSize(kDefaultWindowWidth, kDefaultWindowHeight));
frame->Show(true);
return true;
}
};
wxBEGIN_EVENT_TABLE(wxSfmlCanvas, wxControl)
EVT_IDLE(wxSfmlCanvas::onIdle)
EVT_PAINT(wxSfmlCanvas::onPaint)
EVT_ERASE_BACKGROUND(wxSfmlCanvas::onEraseBackground)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(App);
His example runs as expected but upon closing the wxWidgets windows it crashes with the following error shown below.
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace OBJC, Code 0x1
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libsystem_kernel.dylib 0x00007fff64e7c01e __abort_with_payload + 10
1 libsystem_kernel.dylib 0x00007fff64e77541 abort_with_payload_wrapper_internal + 82
2 libsystem_kernel.dylib 0x00007fff64e774ef abort_with_reason + 22
3 libobjc.A.dylib 0x00007fff63c58674 _objc_fatalv(unsigned long long, unsigned long long, char const*, __va_list_tag*) + 108
4 libobjc.A.dylib 0x00007fff63c58526 _objc_fatal(char const*, ...) + 135
5 libobjc.A.dylib 0x00007fff63c62525 (anonymous namespace)::AutoreleasePoolPage::fastcheck(bool) + 125
6 libobjc.A.dylib 0x00007fff63c4a9f7 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 65
7 com.apple.CoreFoundation 0x00007fff37ab9ee6 _CFAutoreleasePoolPop + 22
8 com.apple.Foundation 0x00007fff39e66a5e -[NSAutoreleasePool drain] + 144
9 org.sfml-dev.sfml-window 0x000000010ffc86a5 drainThreadPool() + 37
10 org.sfml-dev.sfml-window 0x000000010ffc76e4 sf::priv::WindowImplCocoa::~WindowImplCocoa() + 148
11 org.sfml-dev.sfml-window 0x000000010ffc772f sf::priv::WindowImplCocoa::~WindowImplCocoa() + 15
12 org.sfml-dev.sfml-window 0x000000010ffb85ca sf::Window::~Window() + 58
13 org.wxwindows. 0x000000010fe8dc75 wxSfmlCanvas::~wxSfmlCanvas() + 37 (main.cpp:73)
14 org.wxwindows. 0x000000010fe8dc45 Canvas::~Canvas() + 21 (main.cpp:78)
15 org.wxwindows. 0x000000010fe8d505 Canvas::~Canvas() + 21 (main.cpp:78)
16 org.wxwindows. 0x000000010fe8d529 Canvas::~Canvas() + 25 (main.cpp:78)
17 org.wxwindows. 0x000000010fe8e740 AppFrame::~AppFrame() + 304 (memory:2285)
18 org.wxwindows. 0x000000010fe8b335 AppFrame::~AppFrame() + 21 (main.cpp:125)
19 org.wxwindows. 0x000000010fe8b359 AppFrame::~AppFrame() + 25 (main.cpp:125)
20 libwx_osx_cocoau-3.1.2.0.0.dylib 0x000000010fffdd3d wxAppConsoleBase::ProcessIdle() + 157
21 libwx_osx_cocoau-3.1.2.0.0.dylib 0x0000000110238476 wxAppBase::ProcessIdle() + 22
22 libwx_osx_cocoau-3.1.2.0.0.dylib 0x000000011018550a wxApp::ProcessIdle() + 26
23 libwx_osx_cocoau-3.1.2.0.0.dylib 0x00000001100e41fc wxCFEventLoop::OSXCommonModeObserverCallBack(__CFRunLoopObserver*, int, void*) + 92
24 com.apple.CoreFoundation 0x00007fff37b459cd __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
25 com.apple.CoreFoundation 0x00007fff37b45902 __CFRunLoopDoObservers + 452
26 com.apple.CoreFoundation 0x00007fff37ae7558 __CFRunLoopRun + 1469
27 com.apple.CoreFoundation 0x00007fff37ae6d48 CFRunLoopRunSpecific + 463
28 com.apple.HIToolbox 0x00007fff36d7dab5 RunCurrentEventLoopInMode + 293
29 com.apple.HIToolbox 0x00007fff36d7d7eb ReceiveNextEventCommon + 618
30 com.apple.HIToolbox 0x00007fff36d7d568 _BlockUntilNextEventMatchingListInModeWithFilter + 64
31 com.apple.AppKit 0x00007fff35038363 _DPSNextEvent + 997
32 com.apple.AppKit 0x00007fff35037102 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1362
33 com.apple.AppKit 0x00007fff35031165 -[NSApplication run] + 699
34 libwx_osx_cocoau-3.1.2.0.0.dylib 0x00000001101f0dff wxGUIEventLoop::OSXDoRun() + 207
35 libwx_osx_cocoau-3.1.2.0.0.dylib 0x00000001100e4ab1 wxCFEventLoop::DoRun() + 49
36 libwx_osx_cocoau-3.1.2.0.0.dylib 0x000000011002fcae wxEventLoopBase::Run() + 158
37 libwx_osx_cocoau-3.1.2.0.0.dylib 0x000000010fffda13 wxAppConsoleBase::MainLoop() + 99
38 libwx_osx_cocoau-3.1.2.0.0.dylib 0x000000011018555a wxApp::OnRun() + 26
39 libwx_osx_cocoau-3.1.2.0.0.dylib 0x0000000110067763 wxEntry(int&, wchar_t**) + 131
40 org.wxwindows. 0x000000010fe883b6 main + 38 (main.cpp:182)
41 libdyld.dylib 0x00007fff64d24ed9 start + 1
Thread 1:
0 libsystem_pthread.dylib 0x00007fff64f173f8 start_wqthread + 0
1 ??? 0x0000000054485244 0 + 1414025796
Thread 2:: Dispatch queue: NSCGSDisableUpdates
0 libsystem_kernel.dylib 0x00007fff64e5e17a mach_msg_trap + 10
1 libsystem_kernel.dylib 0x00007fff64e5e6d0 mach_msg + 60
2 com.apple.SkyLight 0x00007fff5dc44bc5 CGSUpdateManager::enable_updates_common() + 577
3 com.apple.SkyLight 0x00007fff5dbe7e3b CGSUpdateManager::enable_update(unsigned long long) + 317
4 libdispatch.dylib 0x00007fff64cd5d53 _dispatch_call_block_and_release + 12
5 libdispatch.dylib 0x00007fff64cd6dcf _dispatch_client_callout + 8
6 libdispatch.dylib 0x00007fff64cdd124 _dispatch_lane_serial_drain + 618
7 libdispatch.dylib 0x00007fff64cddbdc _dispatch_lane_invoke + 388
8 libdispatch.dylib 0x00007fff64ce6090 _dispatch_workloop_worker_thread + 603
9 libsystem_pthread.dylib 0x00007fff64f1760b _pthread_wqthread + 409
10 libsystem_pthread.dylib 0x00007fff64f17405 start_wqthread + 13
Thread 3:
0 libsystem_pthread.dylib 0x00007fff64f173f8 start_wqthread + 0
1 ??? 0x0000000054485244 0 + 1414025796
Thread 4:: com.apple.NSEventThread
0 libsystem_kernel.dylib 0x00007fff64e5e17a mach_msg_trap + 10
1 libsystem_kernel.dylib 0x00007fff64e5e6d0 mach_msg + 60
2 com.apple.CoreFoundation 0x00007fff37ae80c2 __CFRunLoopServiceMachPort + 337
3 com.apple.CoreFoundation 0x00007fff37ae7611 __CFRunLoopRun + 1654
4 com.apple.CoreFoundation 0x00007fff37ae6d48 CFRunLoopRunSpecific + 463
5 com.apple.AppKit 0x00007fff35040f89 _NSEventThread + 160
6 libsystem_pthread.dylib 0x00007fff64f18305 _pthread_body + 126
7 libsystem_pthread.dylib 0x00007fff64f1b26f _pthread_start + 70
8 libsystem_pthread.dylib 0x00007fff64f17415 thread_start + 13
Thread 5:
0 libsystem_pthread.dylib 0x00007fff64f173f8 start_wqthread + 0
Thread 0 crashed with X86 Thread State (64-bit):
rax: 0x0000000002000209 rbx: 0x0000000000000080 rcx: 0x00007ffedfd767c8 rdx: 0x0000000000000000
rdi: 0x0000000000000008 rsi: 0x0000000000000001 rbp: 0x00007ffedfd76810 rsp: 0x00007ffedfd767c8
r8: 0x00007febce519d50 r9: 0x0000000000000080 r10: 0x0000000000000000 r11: 0x0000000000000246
r12: 0x0000000000000000 r13: 0x0000000000000000 r14: 0x0000000000000001 r15: 0x0000000000000008
rip: 0x00007fff64e7c01e rfl: 0x0000000000000246 cr2: 0x0000000116681000
Logical CPU: 0
Error Code: 0x02000209
Trap Number: 133
I suspect wxControl and sf::RenderWindows are trying the close the NSView, which comes from the GetHandle() call, at the same time.
-James Harris
I pinged @mantognini already based on your forum post, as he'd know best what is going on here. I tried to understand it, but I don't really following the whole logic and workarounds...
I quickly looked into it.
objc[32687]: autorelease pool page 0x10189b000 corrupted
magic 0x02610f00 0x00006000 0xa2904f60 0x00007fff
should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341
pthread 0x0
should be 0x1000d35c0
Is wx corrupting pthread state?
The crash happens in AutoreleasePoolWrapper.mm's destroyPool() function. But I have no idea what's happening. I tried removing all the pthread related stuff from this file but this hasn't solve the issue (I was expecting to see the bug go away without introducing another one because all allocations are done in the main thread with this test). Instead, I got different values:
objc[36432]: autorelease pool page 0x102071000 corrupted
magic 0x9846a19d 0x00000000 0xfecc1b72 0x4447417b
should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341
pthread 0x10f8a72c272bc602
should be 0x1000d35c0
Maybe someone more familiar with wx could help?
Separating the multiple inheritance of wxSfmlCanvas is something I would like to try in the coming weeks. Because maybe this crash is caused by destructing the wx stuff prior to the sfml::window? After a second thought, maybe switching the order of the inheritance could yield different results? Another approach, I would like to try is rolling back wxWidgets to an earlier version, such as 2.8.12. Maybe this approach could work for now until this one is resolved later?
I will let you know what I find in the coming weeks. In advanced, thank you for your patience and help.
-James Harris
As expected, the objects destruction order doesn't matter. As a result, reversing the inheritance had no impact.
For the second test, I tried separating the inheritance to isolate this issue to the NSView. So, I made sf::RenderWindow into a member of wxSfmlCanvas. Interesting enough, after closing the application, the debug console now outputs the following message.
@"Cannot close SFML area when SFML is integrated in a NSView.\r\n"
@"Cannot close SFML area when SFML is integrated in a NSView.\r\n"
@"objc[6991]: autorelease pool page 0x1018b2000 corrupted\r\n"
@" magic 0x81b800c0 0x00007fff 0x018dfcd8 0x00000001\r\n"
@" should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341\r\n"
@" pthread 0x7fff21f7e158\r\n"
@" should be 0x1001025c0\r\n"
@"\r\n"
It appears that the NSView is being closed before SFML has a chance to shutdown, I think. So, my next test will be to wrap sf::RenderWindow into a singleton class to isolate wx. I'm suspecting wx is closing the NSView while SFML is still using it, possibly releasing resources too soon.
-James Harris
I wrapped sf::RenderWindow into the singleton class, and I was able to isolate the shutdown routines to run three tests.
Shutdown SFML before wx didn't stop the crash from occurring, but instead caused a 'invalid or prematurely-freed autorelease pool' during wx's shutdown. If wx was allowed to shutdown completely while sf::RenderWindow is wrapped in a singleton class the crash also still occurred. The final test actually eliminated the crash and implies a problem with compiler destructor priority between NSView and SFML.
For the last test, I took out the singleton, and declared sf::RenderWindow as a global variable wrapped inside a namespace. Long behold, the application stopped crashing after closing the application. Somewhere in the operating system's shutdown process the destructor order is off.
To conclude, adding __attribute__((destructor[(priority)])) function attributes could be a possible solution.
To conclude, adding attribute((destructor[(priority)])) function attributes could be a possible solution.
Would that be an SFML modification or one in your own code?
This would be a modification to SFML, but I would like to take back my statement about using attribute. After further research today into what NSAutoreleasePool is doing. I found this resource which may help https://github.com/bkaradzic/bgfx/issues/1607 . There may be an issue with NSAutoreleasePool and how SFML'd usage is interfering with wx on a single thread. The resource says it appears to work on multi-thread. Their solution was to move NSAutoreleasePool to another location to prevent conflicts.
Most helpful comment
As expected, the objects destruction order doesn't matter. As a result, reversing the inheritance had no impact.
For the second test, I tried separating the inheritance to isolate this issue to the NSView. So, I made sf::RenderWindow into a member of wxSfmlCanvas. Interesting enough, after closing the application, the debug console now outputs the following message.
It appears that the NSView is being closed before SFML has a chance to shutdown, I think. So, my next test will be to wrap sf::RenderWindow into a singleton class to isolate wx. I'm suspecting wx is closing the NSView while SFML is still using it, possibly releasing resources too soon.
-James Harris