From cfa140fb125b1b0af6d893496727e63fe2e1665b Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 28 Aug 2026 08:19:36 +0400 Subject: [PATCH] window: create Vulkan surface visible on main thread --- .../presentation/window/swapchain.cpp | 76 ++----------------- .../presentation/window/vulkanWindow.cpp | 6 -- src/graphics/presentation/window/window.cpp | 48 +++++------- .../presentation/window/windowInternal.h | 21 +---- 4 files changed, 27 insertions(+), 124 deletions(-) diff --git a/src/graphics/presentation/window/swapchain.cpp b/src/graphics/presentation/window/swapchain.cpp index e3977089a..1a9647307 100644 --- a/src/graphics/presentation/window/swapchain.cpp +++ b/src/graphics/presentation/window/swapchain.cpp @@ -1,30 +1,8 @@ -#include "SDL.h" -#include "SDL_error.h" -#include "SDL_events.h" -#include "SDL_gamecontroller.h" -#include "SDL_hints.h" -#include "SDL_joystick.h" -#include "SDL_keyboard.h" -#include "SDL_keycode.h" -#include "SDL_mouse.h" -#include "SDL_pixels.h" -#include "SDL_rwops.h" -#include "SDL_stdinc.h" -#include "SDL_surface.h" -#include "SDL_thread.h" -#include "SDL_touch.h" -#include "SDL_video.h" -#include "SDL_vulkan.h" #include "common/assert.h" #include "common/common.h" -#include "common/emulatorConfig.h" -#include "common/file.h" #include "common/logging/log.h" #include "common/profiler.h" -#include "common/stringUtils.h" -#include "common/systemInfo.h" #include "common/threads.h" -#include "common/timer.h" #include "graphics/host_gpu/graphicContext.h" #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderContext.h" @@ -34,19 +12,11 @@ #include "graphics/presentation/presenter.h" #include "graphics/presentation/videoOut.h" #include "graphics/presentation/window/windowInternal.h" -#include "libs/controller.h" -#include "loader/systemContent.h" #include -#include -#include -#include -#include #include -#include #include #include -#include #include #include @@ -334,7 +304,6 @@ public: private: void Destroy(); - void RefreshSurfaceSize(); WindowContext& m_window; vk::SwapchainKHR m_handle = nullptr; @@ -396,12 +365,12 @@ struct Presenter::Impl { void Swapchain::Create() { auto& graphics = m_window.graphic_ctx; - EXIT_IF(graphics.screen_width == 0); - EXIT_IF(graphics.screen_height == 0); EXIT_IF(graphics.device == nullptr); EXIT_IF(m_window.surface == nullptr); Common::LockGuard lock(m_window.mutex); + EXIT_IF(graphics.screen_width == 0); + EXIT_IF(graphics.screen_height == 0); const auto& surface = m_window.surface_capabilities; EXIT_NOT_IMPLEMENTED(surface.formats.empty()); @@ -554,30 +523,18 @@ void Swapchain::Destroy() { m_render_complete.clear(); } -void Swapchain::RefreshSurfaceSize() { - int width = 0; - int height = 0; - SDL_Vulkan_GetDrawableSize(m_window.window, &width, &height); - if (width > 0 && height > 0) { - m_window.graphic_ctx.screen_width = static_cast(width); - m_window.graphic_ctx.screen_height = static_cast(height); - } - - m_window.RefreshSurfaceCapabilities(); -} - void Swapchain::Recreate(bool surface_lost) { Destroy(); if (surface_lost) { #if defined(__APPLE__) // Surface recreation goes through SDL_Vulkan_CreateSurface, which touches the // window's view/layer and must run on the main thread on macOS. - m_window.RunOnMainThread([this] { m_window.RecreateSurface(); }, true); + m_window.RunOnMainThread([this] { m_window.RecreateSurface(); }); #else m_window.RecreateSurface(); #endif } - RefreshSurfaceSize(); + m_window.RefreshSurfaceCapabilities(); Create(); } @@ -803,29 +760,6 @@ void Presenter::Present(Frame& frame, bool reuse) { KYTY_PROFILER_FUNCTION(); m_impl->frames.ValidateForPresent(&frame, reuse); - auto& window = m_impl->window; - if (window.window_hidden) { -#if defined(__APPLE__) - // AppKit traps if a window is shown off the main thread; marshal and wait so the - // swapchain below is recreated against a visible window. - window.RunOnMainThread( - [&window] { - window.UpdateIcon(); - SDL_ShowWindow(window.window); - SDL_RaiseWindow(window.window); - }, - true); -#else - window.UpdateIcon(); - - SDL_ShowWindow(window.window); - SDL_RaiseWindow(window.window); -#endif - - window.window_hidden = false; - m_impl->RecoverSwapchain(Swapchain::Status::Recreate); - } - const auto ime_visual = GetImeVisualState(); auto& swapchain = m_impl->swapchain; for (uint32_t attempt = 0; attempt < 2; attempt++) { @@ -848,7 +782,7 @@ void Presenter::Present(Frame& frame, bool reuse) { } m_impl->presented_ime_revision.store(ime_visual.revision, std::memory_order_release); - window.UpdateTitle(); + m_impl->window.UpdateTitle(); m_impl->frames.Release(&frame, true); return; } diff --git a/src/graphics/presentation/window/vulkanWindow.cpp b/src/graphics/presentation/window/vulkanWindow.cpp index 97d8e3b44..6d1e5df6b 100644 --- a/src/graphics/presentation/window/vulkanWindow.cpp +++ b/src/graphics/presentation/window/vulkanWindow.cpp @@ -22,7 +22,6 @@ #include "common/logging/log.h" #include "common/profiler.h" #include "common/stringUtils.h" -#include "common/systemInfo.h" #include "common/threads.h" #include "common/timer.h" #include "graphics/host_gpu/graphicContext.h" @@ -1037,10 +1036,6 @@ void WindowContext::CreateVulkan() { } } - memcpy(device_name, device_properties.deviceName, sizeof(device_name)); - std::snprintf(processor_name, sizeof(processor_name), "%s", - Common::GetSystemInfo().ProcessorName.c_str()); - VulkanInitSubgroupSizeControl(graphic_ctx.physical_device, graphic_ctx); graphic_ctx.device = VulkanCreateDevice(graphic_ctx.physical_device, r, queue_family, @@ -1079,7 +1074,6 @@ void WindowContext::RecreateSurface() { EXIT("Could not recreate the Vulkan surface: %s\n", SDL_GetError()); } surface = native_surface; - RefreshSurfaceCapabilities(); } WindowContext::~WindowContext() { diff --git a/src/graphics/presentation/window/window.cpp b/src/graphics/presentation/window/window.cpp index a6708eda9..78e8a3d15 100644 --- a/src/graphics/presentation/window/window.cpp +++ b/src/graphics/presentation/window/window.cpp @@ -23,7 +23,6 @@ #include "common/file.h" #include "common/logging/log.h" #include "common/profiler.h" -#include "common/stringUtils.h" #include "common/systemInfo.h" #include "common/threads.h" #include "common/timer.h" @@ -39,21 +38,18 @@ #include "libs/controller.h" #include "loader/systemContent.h" -#include -#include #include -#include #include #include #include #include +#include + #define STB_IMAGE_IMPLEMENTATION #define STBI_NO_SIMD #include "stb_image.h" -#include - // IWYU pragma: no_include #define KYTY_ENABLE_DEBUG_PRINTF @@ -197,8 +193,6 @@ std::unique_ptr g_window; } // namespace constexpr const char* KYTY_SDL_WINDOW_CAPTION = "Game"; -constexpr uint32_t KYTY_SDL_WINDOW_FLAGS = - (static_cast(SDL_WINDOW_HIDDEN) | static_cast(SDL_WINDOW_VULKAN)); constexpr int KYTY_SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED; /*NOLINT(hicpp-signed-bitwise)*/ static void SetPause(WindowLoopState& game, bool flag) { @@ -407,6 +401,7 @@ static void GameEventDidEnterForeground(WindowLoopState& game) { void WindowContext::Resize(uint32_t new_width, uint32_t new_height) { EXIT_IF(new_width == 0 || new_height == 0); + Common::LockGuard lock(mutex); graphic_ctx.screen_width = new_width; graphic_ctx.screen_height = new_height; } @@ -728,8 +723,7 @@ void WindowContext::ProcessEvent(double time_s) { } } -#if defined(__APPLE__) -void WindowContext::RunOnMainThread(std::function task, bool wait) { +void WindowContext::RunOnMainThread(std::function task) { if (Common::Thread::IsMainThread()) { task(); return; @@ -747,9 +741,6 @@ void WindowContext::RunOnMainThread(std::function task, bool wait) { event.type = SDL_USEREVENT; SDL_PushEvent(&event); - if (!wait) { - return; - } Common::LockGuard lock(main_task_mutex); while (main_tasks_run < ticket) { main_task_done.Wait(&main_task_mutex); @@ -772,7 +763,6 @@ void WindowContext::DrainMainThreadTasks() { main_tasks_run += tasks.size(); main_task_done.SignalAll(); } -#endif void WindowContext::Run() { Common::Timer timer; @@ -783,9 +773,7 @@ void WindowContext::Run() { loop.paused.store(false, std::memory_order_release); while (!loop.need_exit) { -#if defined(__APPLE__) DrainMainThreadTasks(); -#endif if (loop.paused.load(std::memory_order_acquire)) { if (!timer.IsPaused()) { timer.Pause(); @@ -821,10 +809,7 @@ static void WindowCreate(WindowContext& context) { LOGF("WindowCreate(): width = %d, height = %d\n", width, height); - uint32_t window_flags = KYTY_SDL_WINDOW_FLAGS; - if (Config::FullscreenEnabled()) { - window_flags |= static_cast(SDL_WINDOW_FULLSCREEN_DESKTOP); - } + uint32_t window_flags = WindowContext::InitialWindowFlags(Config::FullscreenEnabled()); #if defined(__APPLE__) // macOS 26 window chrome (CoreUI asset decode, SwiftUI titlebar) has been observed // throwing NSExceptions under Rosetta during the first CATransaction commit. A @@ -836,13 +821,20 @@ static void WindowCreate(WindowContext& context) { context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, KYTY_SDL_WINDOWPOS_CENTERED, KYTY_SDL_WINDOWPOS_CENTERED, width, height, window_flags); - context.window_hidden = true; - if (context.window == nullptr) { EXIT("%s\n", SDL_GetError()); } SDL_SetWindowResizable(context.window, SDL_FALSE); + context.UpdateIcon(); +} + +uint32_t WindowContext::InitialWindowFlags(bool fullscreen) noexcept { + auto flags = static_cast(SDL_WINDOW_VULKAN); + if (fullscreen) { + flags |= static_cast(SDL_WINDOW_FULLSCREEN_DESKTOP); + } + return flags; } Presenter& WindowInit(uint32_t width, uint32_t height) { @@ -959,6 +951,7 @@ void WindowContext::UpdateTitle() { Loader::SystemContentParamSfoGetString("TITLE_ID", title_id, sizeof(title_id)); static bool has_app_ver = Loader::SystemContentParamSfoGetString("APP_VER", app_ver, sizeof(app_ver)); + static const std::string processor_name = Common::GetSystemInfo().ProcessorName; static uint64_t fps_start = Common::Timer::QueryPerformanceCounter(); static uint64_t frame_num = 0; static uint64_t fps_frames = 0; @@ -983,19 +976,14 @@ void WindowContext::UpdateTitle() { fps_frames = 0; } - auto fps = fmt::format( + const auto* device_name = graphic_ctx.GetPhysicalDeviceProperties().deviceName.data(); + auto text = fmt::format( "[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL, build_type, (has_title ? title : ""), (has_title ? ", " : ""), (has_title_id ? title_id : ""), (has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""), device_name, processor_name, frame_num, current_fps); -#if defined(__APPLE__) - // AppKit traps on title changes off the main thread; fire-and-forget keeps present pacing. - RunOnMainThread([this, fps = std::move(fps)] { SDL_SetWindowTitle(window, fps.c_str()); }, - false); -#else - SDL_SetWindowTitle(window, fps.c_str()); -#endif + RunOnMainThread([this, text = std::move(text)] { SDL_SetWindowTitle(window, text.c_str()); }); } } // namespace Libs::Graphics diff --git a/src/graphics/presentation/window/windowInternal.h b/src/graphics/presentation/window/windowInternal.h index 1a9936110..0a2cc6748 100644 --- a/src/graphics/presentation/window/windowInternal.h +++ b/src/graphics/presentation/window/windowInternal.h @@ -9,13 +9,10 @@ #include #include +#include #include #include -#if defined(__APPLE__) -#include -#endif - namespace Libs::Graphics { class Presenter; @@ -39,6 +36,7 @@ struct WindowContext { KYTY_CLASS_NO_COPY(WindowContext); [[nodiscard]] static vk::PhysicalDeviceVulkan13Features RequiredVulkan13Features() noexcept; + [[nodiscard]] static uint32_t InitialWindowFlags(bool fullscreen) noexcept; void CreateVulkan(); void RecreateSurface(); void RefreshSurfaceCapabilities(); @@ -49,36 +47,25 @@ struct WindowContext { void ProcessDisplayEvent(const SDL_DisplayEvent& event); void ProcessEvent(double time_seconds); void Run(); - -#if defined(__APPLE__) - // AppKit only allows window operations (show, icon, title, view/layer changes) on the - // main thread; the present thread marshals them through the SDL main loop with these. - // wait=true blocks until the task has run on the main thread. - void RunOnMainThread(std::function task, bool wait); + // SDL window operations must complete on the main thread. + void RunOnMainThread(std::function task); void DrainMainThreadTasks(); -#endif GraphicContext graphic_ctx; SDL_Window* window = nullptr; - bool window_hidden = true; vk::SurfaceKHR surface = nullptr; SurfaceCapabilities surface_capabilities; std::unique_ptr render_context; std::unique_ptr presenter; WindowLoopState loop; - char device_name[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE] = {0}; - char processor_name[64] = {0}; - Common::Mutex mutex; -#if defined(__APPLE__) Common::Mutex main_task_mutex; Common::CondVar main_task_done; std::vector> main_tasks; // guarded by main_task_mutex uint64_t main_tasks_queued = 0; // guarded by main_task_mutex uint64_t main_tasks_run = 0; // guarded by main_task_mutex -#endif }; } // namespace Libs::Graphics