Fix macOS build and run issues + input/simulation deadlock#1812
Open
lugt wants to merge 1 commit into
Open
Conversation
Fixes the build and run problems on macOS (Tahoe / Qt 6.11 / Homebrew LLVM clang), based on PR SFTtech#1777 by Skosulor, plus a deadlock fix exposed by the resulting parallel simulation/presenter threads. Three categories of macOS fixes (from PR SFTtech#1777): 1. Run the presenter on the main thread. QGuiApplication must be created and operated on the main thread; on macOS 26 (Tahoe) the platform plugin asserts ("setting the main menu on a non-main thread") in -[NSMenu _setMenuName:] otherwise. The simulation is moved to a worker thread instead. 2. Remove the guard that stopped Qt events from being processed on macOS (gui_application_impl.cpp's processEvents was wrapped in #ifndef __APPLE__). 3. Fix shaders that crashed glLinkProgram on macOS. The macOS OpenGL driver crashes (SIGSEGV in glpLLVMCGSwitchStatement) when compiling a switch statement in world2d.frag.glsl; switched to if/else. Also upgraded the GUI shaders from GLSL 1.2 (#version 120, attribute/ varying) to 3.3 core (#version 330 core, in/out, layout(location=)), and renamed the `texture` uniform to `tex` to avoid the GLSL reserved keyword. Deadlock fix (exposed by running simulation and presenter in parallel): Controller::process held Controller::mutex while calling bind.transform(), which calls back into EventLoop::create_event and thus takes EventLoop::mutex. The simulation thread takes the locks in the opposite order (EventLoop -> Controller, via EventLoop::reach_time -> DragSelectHandler -> Controller::set_selected), producing an AB-BA deadlock a few seconds into mouse interaction. Fixed by narrowing Controller::process's lock scope to only protect the outqueue operations; bind.transform() now runs without holding Controller::mutex, so both threads take the locks in the order EventLoop -> Controller. Review feedback (heinezen) addressed: - Engine: thread functions (time loop, simulation) extracted into run_time_loop()/run_simulation(); all thread spawning moved into Engine::loop(); comments explain why the main thread runs the presenter in FULL mode (Qt requirement) and the simulation in headless mode. The thread-count log is now printed after spawning. - window_settings moved from Engine into Presenter (stored as a member, passed via the Presenter constructor); Presenter::run() and init_graphics() no longer take it as a parameter. - gui.cpp: removed the static constexpr for the texture uniform name; the uniform name is passed inline (now "tex"). - maptexture.frag.glsl: uniform renamed texture_ -> tex. Two review suggestions intentionally not applied, with rationale to be discussed in the PR: - identity.vert.glsl: kept `gl_Position = vertex_position;`. In GLSL 330 core, gl_Position does not automatically contain the vertex position; removing it would collapse the GUI quad to the origin. - world2d.frag.glsl: kept if/else instead of switch, with a comment explaining the macOS driver crash. Same for the demo_2/4_obj shaders. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
This was referenced Jul 17, 2026
lugt
added a commit
to lugt/openage
that referenced
this pull request
Jul 18, 2026
The test GUI (assets/test/qml/main.qml) imports yay.sfttech.openage as OA, but that module is never registered in libopenage (only yay.sfttech.livereload is) and OA is not referenced anywhere in the file. On macOS, once app.processEvents() runs (PR SFTtech#1812), this failed import aborts QML loading, which makes the presenter draw loop exit immediately and the game never reaches a playable state. Drop the import so the GUI loads. Workaround for the upstream "TODO: Do not use test GUI" (presenter.cpp); the real GUI is unfinished. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Continuation of #1777 (Fix macos build and run issues by @Skosulor), rebased onto current master with the review feedback addressed and an additional deadlock fix that the parallel threading exposes on macOS.
What this does
macOS build/run fixes (from #1777)
QGuiApplicationmust be created and operated on the main thread. On macOS 26 (Tahoe) the platform plugin asserts in-[NSMenu _setMenuName:]("API misuse: setting the main menu on a non-main thread") otherwise. The simulation is moved to a worker thread instead.#ifndef __APPLE__guard that stopped Qt events from being processed on macOS (gui_application_impl.cpp).glLinkProgramon macOS. The macOS OpenGL driver crashes (SIGSEGV inglpLLVMCGSwitchStatementduringglLinkProgram) when compiling aswitchstatement inworld2d.frag.glsl— switched toif/else. Also upgraded the GUI shaders from GLSL 1.2 (#version 120,attribute/varying) to 3.3 core (in/out,layout(location=)), and renamed thetextureuniform totexto avoid the GLSL reserved keyword.Deadlock fix (new, exposed by parallel sim/presenter)
Once the simulation runs in a worker thread and the presenter on the main thread, mouse interaction deadlocks within a few seconds:
Controller::processholdsController::mutex, then callsbind.transform()→EventLoop::create_event(takesEventLoop::mutex). Lock order: Controller → EventLoop.EventLoop::reach_timeholdsEventLoop::mutex, executesDragSelectHandler::invoke→select_cb→Controller::set_selected(takesController::mutex). Lock order: EventLoop → Controller.Classic AB-BA deadlock (confirmed via
lldb attach+thread backtrace all, both threads in__psynch_mutexwait).Fix: narrow
Controller::process's lock scope to only protect theoutqueueoperations;bind.transform()now runs without holdingController::mutex. Both threads then take the locks in the order EventLoop → Controller, eliminating the cycle. The bindings' callbacks (get_controlled,get_selected,reset_drag_select, …) already takeController::mutexthemselves and are safe to call unlocked because it is arecursive_mutex.This is the same class of issue @Skosulor reported in #1777 (
mutex lock failed: Invalid argument), just triggered through the drag-select path.Review feedback (@heinezen) addressed
run_time_loop()/run_simulation(); all thread spawning moved intoEngine::loop(); comments explain why the main thread runs the presenter in FULL mode (Qt requirement) and the simulation in headless mode. Thread-count log is now printed after spawning (so the number is correct).window_settingsmoved fromEngineintoPresenter(stored as a member, passed via the Presenter constructor);Presenter::run()andinit_graphics()no longer take it as a parameter.gui.cpp: removed thestatic constexprfor the texture uniform name; the name is passed inline (now\"tex\").maptexture.frag.glsl: uniform renamedtexture_→tex.Two suggestions intentionally not applied — would like your input
identity.vert.glsl: keptgl_Position = vertex_position;. In GLSL 330 core,gl_Positiondoes not automatically contain the current vertex position (it's a write-only output with undefined default); removing it would collapse the GUI quad to the origin(0,0,0,1). This may be a confusion with compatibility-profilegl_Vertex. Could you double-check @heinezen?world2d.frag.glsl/demo_2_obj/demo_4_obj: keptif/elseinstead ofswitch, with a comment explaining the macOS driver crash (glpLLVMCGSwitchStatementSIGSEGV duringglLinkProgram). This is a driver bug, not a shader-authoring issue — confirmed by the crash stack. Aswitchreliably crashes on macOS even though it's valid GLSL.Verification
Built and run on macOS 26.3 (Apple Silicon), Homebrew Qt 6.11.1 + LLVM clang 22.1.8, Python 3.14. Tested:
Draw loop exited→Game simulation stopped→Time loop stopped→presenter exited).renderer_stresstest 0runs end-to-end (shaders link, entities spawn, FPS stable), confirming the shader fixes.Supersedes #1777. Happy to split the deadlock fix into a separate PR if preferred.
🤖 Generated with Claude Code