diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 239c85c4..395523fc 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -272,6 +272,10 @@ if (SWA_D3D12) target_compile_definitions(UnleashedRecomp PRIVATE SWA_D3D12) endif() +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + target_compile_definitions(UnleashedRecomp PRIVATE SDL_VULKAN_ENABLED) +endif() + find_package(directx-dxc REQUIRED) if (SWA_D3D12) diff --git a/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h b/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h index f0e94fd3..c4e87727 100644 --- a/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h +++ b/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h @@ -29,12 +29,18 @@ typedef struct _NSWindow NSWindow; #endif +#ifdef SDL_VULKAN_ENABLED +#include +#endif + namespace plume { #if defined(_WIN64) // Native HWND handle to the target window. typedef HWND RenderWindow; #elif defined(__ANDROID__) typedef ANativeWindow* RenderWindow; +#elif defined(SDL_VULKAN_ENABLED) + typedef SDL_Window *RenderWindow; #elif defined(__linux__) struct RenderWindow { Display* display; diff --git a/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp b/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp index 497a8a40..89334cef 100644 --- a/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp +++ b/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp @@ -1989,6 +1989,13 @@ namespace plume { fprintf(stderr, "vkCreateWin32SurfaceKHR failed with error code 0x%X.\n", res); return; } +# elif defined(SDL_VULKAN_ENABLED) + VulkanInterface *renderInterface = commandQueue->device->renderInterface; + SDL_bool sdlRes = SDL_Vulkan_CreateSurface(renderWindow, renderInterface->instance, &surface); + if (sdlRes == SDL_FALSE) { + fprintf(stderr, "SDL_Vulkan_CreateSurface failed with error %s.\n", SDL_GetError()); + return; + } # elif defined(__ANDROID__) assert(renderWindow != nullptr); VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {}; @@ -2312,6 +2319,8 @@ namespace plume { GetClientRect(renderWindow, &rect); dstWidth = rect.right - rect.left; dstHeight = rect.bottom - rect.top; +# elif defined(SDL_VULKAN_ENABLED) + SDL_GetWindowSize(renderWindow, (int *)(&dstWidth), (int *)(&dstHeight)); # elif defined(__ANDROID__) dstWidth = ANativeWindow_getWidth(renderWindow); dstHeight = ANativeWindow_getHeight(renderWindow); @@ -4073,7 +4082,11 @@ namespace plume { // VulkanInterface +#if SDL_VULKAN_ENABLED + VulkanInterface::VulkanInterface(RenderWindow sdlWindow) { +#else VulkanInterface::VulkanInterface() { +#endif VkResult res = volkInitialize(); if (res != VK_SUCCESS) { fprintf(stderr, "volkInitialize failed with error code 0x%X.\n", res); @@ -4100,11 +4113,31 @@ namespace plume { std::vector availableExtensions(extensionCount); vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, availableExtensions.data()); - std::unordered_set missingRequiredExtensions = RequiredInstanceExtensions; + std::unordered_set requiredExtensions = RequiredInstanceExtensions; std::unordered_set supportedOptionalExtensions; # if DLSS_ENABLED const std::unordered_set dlssExtensions = DLSS::getRequiredInstanceExtensionsVulkan(); # endif + +# if SDL_VULKAN_ENABLED + // Push the extensions specified by SDL as required. + // SDL2 has this awkward requirement for the window to pull the extensions from. + // This can be removed when upgrading to SDL3. + if (sdlWindow != nullptr) { + uint32_t sdlVulkanExtensionCount = 0; + if (SDL_Vulkan_GetInstanceExtensions(sdlWindow, &sdlVulkanExtensionCount, nullptr)) { + std::vector sdlVulkanExtensions; + sdlVulkanExtensions.resize(sdlVulkanExtensionCount); + if (SDL_Vulkan_GetInstanceExtensions(sdlWindow, &sdlVulkanExtensionCount, (const char **)(sdlVulkanExtensions.data()))) { + for (char *sdlVulkanExtension : sdlVulkanExtensions) { + requiredExtensions.insert(sdlVulkanExtension); + } + } + } + } +# endif + + std::unordered_set missingRequiredExtensions = requiredExtensions; for (uint32_t i = 0; i < extensionCount; i++) { const std::string extensionName(availableExtensions[i].extensionName); missingRequiredExtensions.erase(extensionName); @@ -4129,7 +4162,7 @@ namespace plume { } std::vector enabledExtensions; - for (const std::string &extension : RequiredInstanceExtensions) { + for (const std::string &extension : requiredExtensions) { enabledExtensions.push_back(extension.c_str()); } @@ -4192,8 +4225,15 @@ namespace plume { // Global creation function. +#if SDL_VULKAN_ENABLED + std::unique_ptr CreateVulkanInterface(RenderWindow sdlWindow) { + std::unique_ptr createdInterface = std::make_unique(sdlWindow); + return createdInterface->isValid() ? std::move(createdInterface) : nullptr; + } +#else std::unique_ptr CreateVulkanInterface() { std::unique_ptr createdInterface = std::make_unique(); return createdInterface->isValid() ? std::move(createdInterface) : nullptr; } +#endif }; diff --git a/UnleashedRecomp/gpu/rhi/plume_vulkan.h b/UnleashedRecomp/gpu/rhi/plume_vulkan.h index 16cf3647..6ffe5aa3 100644 --- a/UnleashedRecomp/gpu/rhi/plume_vulkan.h +++ b/UnleashedRecomp/gpu/rhi/plume_vulkan.h @@ -422,7 +422,12 @@ namespace plume { VkApplicationInfo appInfo = {}; RenderInterfaceCapabilities capabilities; +# if SDL_VULKAN_ENABLED + VulkanInterface(RenderWindow sdlWindow); +# else VulkanInterface(); +# endif + ~VulkanInterface() override; std::unique_ptr createDevice() override; const RenderInterfaceCapabilities &getCapabilities() const override; diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index d07c4d87..5517bc14 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -83,7 +83,11 @@ namespace plume #ifdef SWA_D3D12 extern std::unique_ptr CreateD3D12Interface(); #endif +#ifdef SDL_VULKAN_ENABLED + extern std::unique_ptr CreateVulkanInterface(RenderWindow sdlWindow); +#else extern std::unique_ptr CreateVulkanInterface(); +#endif } #pragma pack(push, 1) @@ -1306,7 +1310,7 @@ static void CreateImGuiBackend() static void BeginCommandList(); -void Video::CreateHostDevice() +void Video::CreateHostDevice(bool sdlVideoDefault) { for (uint32_t i = 0; i < 16; i++) g_inputSlots[i].index = i; @@ -1314,7 +1318,7 @@ void Video::CreateHostDevice() IMGUI_CHECKVERSION(); ImGui::CreateContext(); - GameWindow::Init(); + GameWindow::Init(sdlVideoDefault); #ifdef SWA_D3D12 g_vulkan = DetectWine() || Config::GraphicsAPI == EGraphicsAPI::Vulkan; @@ -1322,10 +1326,15 @@ void Video::CreateHostDevice() LoadEmbeddedResources(); -#ifdef SWA_D3D12 - g_interface = g_vulkan ? CreateVulkanInterface() : CreateD3D12Interface(); + if (g_vulkan) +#ifdef SDL_VULKAN_ENABLED + g_interface = CreateVulkanInterface(GameWindow::s_renderWindow); #else - g_interface = CreateVulkanInterface(); + g_interface = CreateVulkanInterface(); +#endif +#ifdef SWA_D3D12 + else + g_interface = CreateD3D12Interface(); #endif g_device = g_interface->createDevice(); diff --git a/UnleashedRecomp/gpu/video.h b/UnleashedRecomp/gpu/video.h index 481e251f..9270cf44 100644 --- a/UnleashedRecomp/gpu/video.h +++ b/UnleashedRecomp/gpu/video.h @@ -14,7 +14,7 @@ using namespace plume; struct Video { - static void CreateHostDevice(); + static void CreateHostDevice(bool sdlVideoDefault); static void HostPresent(); static void StartPipelinePrecompilation(); static void WaitForGPU(); diff --git a/UnleashedRecomp/main.cpp b/UnleashedRecomp/main.cpp index 2387886e..b5ab376e 100644 --- a/UnleashedRecomp/main.cpp +++ b/UnleashedRecomp/main.cpp @@ -145,10 +145,12 @@ int main(int argc, char *argv[]) bool forceInstaller = false; bool forceDLCInstaller = false; + bool sdlVideoDefault = false; for (uint32_t i = 1; i < argc; i++) { forceInstaller = forceInstaller || (strcmp(argv[i], "--install") == 0); forceDLCInstaller = forceDLCInstaller || (strcmp(argv[i], "--install-dlc") == 0); + sdlVideoDefault = sdlVideoDefault || (strcmp(argv[i], "--sdl-video-default") == 0); } Config::Load(); @@ -159,7 +161,7 @@ int main(int argc, char *argv[]) bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; if (runInstallerWizard) { - Video::CreateHostDevice(); + Video::CreateHostDevice(sdlVideoDefault); if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller)) { @@ -175,7 +177,7 @@ int main(int argc, char *argv[]) uint32_t entry = LdrLoadModule(std::u8string_view((const char8_t*)(modulePath))); if (!runInstallerWizard) - Video::CreateHostDevice(); + Video::CreateHostDevice(sdlVideoDefault); Video::StartPipelinePrecompilation(); diff --git a/UnleashedRecomp/ui/game_window.cpp b/UnleashedRecomp/ui/game_window.cpp index 6f49e7c8..dfed59ce 100644 --- a/UnleashedRecomp/ui/game_window.cpp +++ b/UnleashedRecomp/ui/game_window.cpp @@ -147,9 +147,26 @@ int Window_OnSDLEvent(void*, SDL_Event* event) return 0; } -void GameWindow::Init() +void GameWindow::Init(bool sdlVideoDefault) { - SDL_InitSubSystem(SDL_INIT_VIDEO); +#ifdef __linux__ + if (!sdlVideoDefault) + { + int videoRes = SDL_VideoInit("wayland"); + if (videoRes != 0) + SDL_VideoInit(nullptr); + } +#else + else + { + SDL_VideoInit(nullptr); + } +#endif + + const char* videoDriverName = SDL_GetCurrentVideoDriver(); + if (videoDriverName != nullptr) + fmt::println("SDL Video Driver: {}", videoDriverName); + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); SDL_AddEventWatch(Window_OnSDLEvent, s_pWindow); #ifdef _WIN32 @@ -185,6 +202,8 @@ void GameWindow::Init() #if defined(_WIN32) s_renderWindow = info.info.win.window; SetDarkTitleBar(true); +#elif defined(SDL_VULKAN_ENABLED) + s_renderWindow = s_pWindow; #elif defined(__linux__) s_renderWindow = { info.info.x11.display, info.info.x11.window }; #else diff --git a/UnleashedRecomp/ui/game_window.h b/UnleashedRecomp/ui/game_window.h index 21f86c3d..82ad1446 100644 --- a/UnleashedRecomp/ui/game_window.h +++ b/UnleashedRecomp/ui/game_window.h @@ -199,6 +199,10 @@ public: if (Config::Fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; +#ifdef SDL_VULKAN_ENABLED + flags |= SDL_WINDOW_VULKAN; +#endif + return flags; } @@ -299,6 +303,6 @@ public: return false; } - static void Init(); + static void Init(bool sdlVideoDefault); static void Update(); };