mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
window: create Vulkan surface visible on main thread
This commit is contained in:
@@ -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 <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <fmt/format.h>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <vulkan/vk_platform.h>
|
||||
|
||||
@@ -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<uint32_t>(width);
|
||||
m_window.graphic_ctx.screen_height = static_cast<uint32_t>(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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <vulkan/vk_platform.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_SIMD
|
||||
#include "stb_image.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
// IWYU pragma: no_include <intrin.h>
|
||||
|
||||
#define KYTY_ENABLE_DEBUG_PRINTF
|
||||
@@ -197,8 +193,6 @@ std::unique_ptr<WindowContext> g_window;
|
||||
} // namespace
|
||||
|
||||
constexpr const char* KYTY_SDL_WINDOW_CAPTION = "Game";
|
||||
constexpr uint32_t KYTY_SDL_WINDOW_FLAGS =
|
||||
(static_cast<uint32_t>(SDL_WINDOW_HIDDEN) | static_cast<uint32_t>(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<void()> task, bool wait) {
|
||||
void WindowContext::RunOnMainThread(std::function<void()> task) {
|
||||
if (Common::Thread::IsMainThread()) {
|
||||
task();
|
||||
return;
|
||||
@@ -747,9 +741,6 @@ void WindowContext::RunOnMainThread(std::function<void()> 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<uint32_t>(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<uint32_t>(SDL_WINDOW_VULKAN);
|
||||
if (fullscreen) {
|
||||
flags |= static_cast<uint32_t>(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
|
||||
|
||||
@@ -9,13 +9,10 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <functional>
|
||||
#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<void()> task, bool wait);
|
||||
// SDL window operations must complete on the main thread.
|
||||
void RunOnMainThread(std::function<void()> 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<RenderContext> render_context;
|
||||
std::unique_ptr<Presenter> 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<std::function<void()>> 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
|
||||
|
||||
Reference in New Issue
Block a user