From 47f3bdc41f070f9977745cc387658e6a1f79ae2d Mon Sep 17 00:00:00 2001 From: Jerome Haxhiaj Date: Tue, 1 Sep 2026 14:46:30 +0200 Subject: [PATCH] display: synchronize macOS SDL window creation The GL switch path invokes the window-create callback and then immediately creates the SDL GL context, initializes the QEMU GL shader, and creates the surface texture. The macOS display bridge previously used fork_on_systemc(), so the callback could return before the SystemC thread had created the native SDL window. This allowed GL setup and the first framebuffer update to race with window creation and could leave an invalid display state or crash during GL refresh. After simulation starts, release QEMU's I/O-thread lock while synchronously running the window-create operation on the SystemC thread, then reacquire the lock before returning to the GL switch path. This preserves the required macOS main-thread affinity while guaranteeing that the window exists before context and texture creation continue. During elaboration, retain the non-blocking behavior to avoid waiting for a SystemC job before the simulation kernel is running. Signed-off-by: Jerome Haxhiaj --- qemu-components/display/src/display.cc | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/qemu-components/display/src/display.cc b/qemu-components/display/src/display.cc index c46d8cfa..a7e4bf75 100644 --- a/qemu-components/display/src/display.cc +++ b/qemu-components/display/src/display.cc @@ -108,8 +108,23 @@ void MainThreadQemuDisplay::window_create(DisplayChangeListener* dcl) { qemu::LibQemu& lib = inst->get(); MainThreadQemuDisplay* display = reinterpret_cast(lib.dcl_new(dcl).get_user_data()); - // SDL2 window create should run on main-thread - display->m_on_sysc.fork_on_systemc([&lib, dcl]() { lib.sdl2_window_create(dcl); }); + + /* + * GL switch continues immediately with context and texture creation, so + * the macOS window must exist before the callback returns. During + * elaboration, keep the callback asynchronous to avoid the startup + * deadlock; once simulation is running, release BQL while synchronously + * rendezvousing with the SystemC thread. + */ + if (display->m_simulation_started) { + lib.unlock_iothread(); + } + + display->m_on_sysc.run_on_sysc([&lib, dcl]() { lib.sdl2_window_create(dcl); }, display->m_simulation_started); + + if (display->m_simulation_started) { + lib.lock_iothread(); + } } void MainThreadQemuDisplay::window_destroy(DisplayChangeListener* dcl)