This commit is contained in:
BeezBumba 2026-07-02 01:45:54 -04:00 committed by GitHub
commit 298c77df4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 510 additions and 306 deletions

68
CMake/FindWayland.cmake Normal file
View File

@ -0,0 +1,68 @@
# Try to find Wayland on a Unix system
#
# This will define:
#
# Wayland_FOUND - True if Wayland is found
#
# The following imported targets:
# Wayland::Client - Imported Client
# Wayland::Server - Imported Server
# Wayland::Egl - Imported Egl
# Wayland::Cursor - Imported Cursor
#
# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
IF (NOT WIN32)
IF (WAYLAND_INCLUDE_DIR AND WAYLAND_LIBRARIES)
# In the cache already
SET(WAYLAND_FIND_QUIETLY TRUE)
ENDIF ()
FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h)
FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h)
FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h)
FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h)
FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client)
FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server)
FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl)
FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor)
include(FindPackageHandleStandardArgs)
# FIND_PACKAGE_HANDLE_STANDARD_ARGS is just meant to find the main package and set package found. Not set variables or find individual libs
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Wayland REQUIRED_VARS
WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR
WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR
WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR
WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR
)
if (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARIES AND NOT TARGET Wayland::Client)
add_library(Wayland::Client UNKNOWN IMPORTED)
set_target_properties(Wayland::Client PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CLIENT_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_CLIENT_LIBRARIES}")
endif()
if (WAYLAND_SERVER_INCLUDE_DIR AND WAYLAND_SERVER_LIBRARIES AND NOT TARGET Wayland::Server)
add_library(Wayland::Server UNKNOWN IMPORTED)
set_target_properties(Wayland::Server PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_SERVER_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_SERVER_LIBRARIES}")
endif()
if (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_LIBRARIES AND NOT TARGET Wayland::Egl)
add_library(Wayland::Egl UNKNOWN IMPORTED)
set_target_properties(Wayland::Egl PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_EGL_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_EGL_LIBRARIES}")
endif()
if (WAYLAND_CURSOR_INCLUDE_DIR AND WAYLAND_CURSOR_LIBRARIES AND NOT TARGET Wayland::Cursor)
add_library(Wayland::Cursor UNKNOWN IMPORTED)
set_target_properties(Wayland::Cursor PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CURSOR_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_CURSOR_LIBRARIES}")
endif()
MARK_AS_ADVANCED(
WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES
)
ENDIF ()

View File

@ -67,6 +67,7 @@ set(DOLPHIN_DEFAULT_UPDATE_TRACK "" CACHE STRING "Name of the default update tra
if(UNIX AND NOT APPLE AND NOT ANDROID)
option(ENABLE_X11 "Enables X11 Support" ON)
option(ENABLE_WAYLAND "Enables Wayland support" ON)
endif()
if(NOT WIN32 AND NOT APPLE AND NOT HAIKU)
option(ENABLE_EGL "Enables EGL OpenGL Interface" ON)
@ -508,6 +509,16 @@ if(ENABLE_X11)
endif()
endif()
if(ENABLE_WAYLAND)
find_package(Wayland)
if(Wayland_FOUND)
add_definitions(-DHAVE_WAYLAND=1)
message(STATUS "Wayland support enabled")
else()
message(WARNING "Wayland support enabled but not found. This build will not support Wayland.")
endif()
endif()
if(ENABLE_EGL)
find_package(EGL)
if(EGL_FOUND)

View File

@ -86,6 +86,8 @@ Common::Flag s_is_booting;
bool s_game_metadata_is_valid = false;
} // Anonymous namespace
static float GetRenderSurfaceScale(JNIEnv* env);
void UpdatePointer()
{
// Update touch pointer
@ -448,7 +450,11 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang
__android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "Error: Surface is null.");
if (g_presenter)
g_presenter->ChangeSurface(s_surf);
{
const u32 width = s_surf ? static_cast<u32>(ANativeWindow_getWidth(s_surf)) : 0;
const u32 height = s_surf ? static_cast<u32>(ANativeWindow_getHeight(s_surf)) : 0;
g_presenter->ChangeSurface(s_surf, width, height, GetRenderSurfaceScale(env));
}
s_surface_cv.notify_all();
}
@ -471,7 +477,11 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
std::lock_guard surface_guard(s_surface_lock);
if (g_presenter)
g_presenter->ChangeSurface(nullptr);
{
g_presenter->ChangeSurface(nullptr, g_presenter->GetBackbufferWidth(),
g_presenter->GetBackbufferHeight(),
g_presenter->GetBackbufferScale());
}
if (s_surf)
{

View File

@ -306,6 +306,13 @@ if(ENABLE_EGL AND EGL_FOUND)
target_link_libraries(common PUBLIC ${EGL_LIBRARIES})
endif()
if(ENABLE_WAYLAND AND WAYLAND_FOUND AND ENABLE_EGL AND EGL_FOUND)
target_sources(common PRIVATE
GL/GLInterface/EGLWayland.cpp
GL/GLInterface/EGLWayland.h
)
endif()
if(WIN32)
target_sources(common PRIVATE
CompatPatches.cpp

View File

@ -19,6 +19,9 @@
#if HAVE_X11
#include "Common/GL/GLInterface/EGLX11.h"
#endif
#if HAVE_EGL && HAVE_WAYLAND
#include "Common/GL/GLInterface/EGLWayland.h"
#endif
#if defined(ANDROID)
#include "Common/GL/GLInterface/EGLAndroid.h"
#endif
@ -54,12 +57,15 @@ bool GLContext::ClearCurrent()
return false;
}
void GLContext::Update()
void GLContext::Update(u32 width, u32 height)
{
m_backbuffer_width = width;
m_backbuffer_height = height;
}
void GLContext::UpdateSurface(void* window_handle)
void GLContext::UpdateSurface(void* window_handle, u32 width, u32 height)
{
Update(width, height);
}
void GLContext::Swap()
@ -100,6 +106,12 @@ std::unique_ptr<GLContext> GLContext::Create(const WindowSystemInfo& wsi, bool s
if (wsi.type == WindowSystemType::X11)
context = std::make_unique<GLContextEGLX11>();
#endif
#endif
#if HAVE_EGL && HAVE_WAYLAND
if (wsi.type == WindowSystemType::Wayland)
context = std::make_unique<GLContextEGLWayland>();
#endif
#if HAVE_EGL
if (wsi.type == WindowSystemType::Headless || wsi.type == WindowSystemType::FBDev)
context = std::make_unique<GLContextEGL>();
#endif

View File

@ -36,8 +36,8 @@ public:
virtual bool MakeCurrent();
virtual bool ClearCurrent();
virtual void Update();
virtual void UpdateSurface(void* window_handle);
virtual void Update(u32 width, u32 height);
virtual void UpdateSurface(void* window_handle, u32 width, u32 height);
virtual void Swap();
virtual void SwapInterval(int interval);

View File

@ -28,7 +28,7 @@ public:
bool MakeCurrent() override;
bool ClearCurrent() override;
void Update() override;
void Update(u32 width, u32 height) override;
void Swap() override;
void SwapInterval(int interval) override;

View File

@ -5,27 +5,7 @@
#include "Common/Logging/Log.h"
// UpdateCachedDimensions and AttachContextToView contain calls to UI APIs, so they must only be
// called from the main thread or they risk crashing!
static bool UpdateCachedDimensions(NSView* view, u32* width, u32* height)
{
NSWindow* window = [view window];
NSSize size = [view frame].size;
const CGFloat scale = [window backingScaleFactor];
u32 new_width = static_cast<u32>(size.width * scale);
u32 new_height = static_cast<u32>(size.height * scale);
if (*width == new_width && *height == new_height)
return false;
*width = new_width;
*height = new_height;
return true;
}
static bool AttachContextToView(NSOpenGLContext* context, NSView* view, u32* width, u32* height)
static bool AttachContextToView(NSOpenGLContext* context, NSView* view)
{
// Enable high-resolution display support.
[view setWantsBestResolutionOpenGLSurface:YES];
@ -37,8 +17,6 @@ static bool AttachContextToView(NSOpenGLContext* context, NSView* view, u32* wid
return false;
}
(void)UpdateCachedDimensions(view, width, height);
[context setView:view];
return true;
@ -98,16 +76,18 @@ bool GLContextAGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool cor
m_view = static_cast<NSView*>(wsi.render_surface);
m_opengl_mode = Mode::OpenGL;
m_backbuffer_width = wsi.render_surface_width;
m_backbuffer_height = wsi.render_surface_height;
__block bool success;
if ([NSThread isMainThread])
{
success = AttachContextToView(m_context, m_view, &m_backbuffer_width, &m_backbuffer_height);
success = AttachContextToView(m_context, m_view);
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
success = AttachContextToView(m_context, m_view, &m_backbuffer_width, &m_backbuffer_height);
success = AttachContextToView(m_context, m_view);
});
}
@ -150,16 +130,11 @@ bool GLContextAGL::ClearCurrent()
return true;
}
void GLContextAGL::Update()
void GLContextAGL::Update(u32 width, u32 height)
{
if (!m_view)
return;
GLContext::Update(width, height);
dispatch_sync(dispatch_get_main_queue(), ^{
if (UpdateCachedDimensions(m_view, &m_backbuffer_width, &m_backbuffer_height))
{
[m_context update];
}
[m_context update];
});
}

View File

@ -70,7 +70,7 @@ void GLContextBGL::Swap()
m_gl->SwapBuffers();
}
void GLContextBGL::Update()
void GLContextBGL::Update(u32, u32)
{
m_gl->LockLooper();
BRect size = m_gl->Frame();

View File

@ -18,7 +18,7 @@ public:
bool MakeCurrent() override;
bool ClearCurrent() override;
void Update() override;
void Update(u32, u32) override;
void Swap() override;

View File

@ -145,6 +145,8 @@ bool GLContextEGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool cor
bool supports_core_profile = false;
m_wsi = wsi;
m_backbuffer_width = wsi.render_surface_width;
m_backbuffer_height = wsi.render_surface_height;
m_egl_display = OpenEGLDisplay();
if (!m_egl_display)
{
@ -346,8 +348,9 @@ bool GLContextEGL::MakeCurrent()
return eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface, m_egl_context);
}
void GLContextEGL::UpdateSurface(void* window_handle)
void GLContextEGL::UpdateSurface(void* window_handle, u32 width, u32 height)
{
GLContext::UpdateSurface(window_handle, width, height);
m_wsi.render_surface = window_handle;
ClearCurrent();
DestroyWindowSurface();

View File

@ -22,7 +22,7 @@ public:
bool MakeCurrent() override;
bool ClearCurrent() override;
void UpdateSurface(void* window_handle) override;
void UpdateSurface(void* window_handle, u32 width, u32 height) override;
void Swap() override;
void SwapInterval(int interval) override;

View File

@ -15,7 +15,5 @@ EGLNativeWindowType GLContextEGLAndroid::GetEGLNativeWindow(EGLConfig config)
EGLint format;
eglGetConfigAttrib(m_egl_display, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(static_cast<ANativeWindow*>(m_wsi.render_surface), 0, 0, format);
m_backbuffer_width = ANativeWindow_getWidth(static_cast<ANativeWindow*>(m_wsi.render_surface));
m_backbuffer_height = ANativeWindow_getHeight(static_cast<ANativeWindow*>(m_wsi.render_surface));
return static_cast<EGLNativeWindowType>(m_wsi.render_surface);
}

View File

@ -0,0 +1,47 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/EGLWayland.h"
#include "Common/MsgHandler.h"
GLContextEGLWayland::~GLContextEGLWayland()
{
if (m_window)
m_wl_egl_window_destroy(m_window);
}
void GLContextEGLWayland::Update(u32 width, u32 height)
{
GLContext::Update(width, height);
if (m_window)
m_wl_egl_window_resize(m_window, width, height, 0, 0);
}
EGLDisplay GLContextEGLWayland::OpenEGLDisplay()
{
return eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wsi.display_connection));
}
EGLNativeWindowType GLContextEGLWayland::GetEGLNativeWindow(EGLConfig config)
{
if (!m_wayland_lib.IsOpen())
{
std::string name = Common::DynamicLibrary::GetVersionedFilename("wayland-egl", 1);
m_wayland_lib.Open(name.c_str());
if (!m_wayland_lib.IsOpen())
{
PanicAlertFmt("Failed to load {}", name);
return reinterpret_cast<EGLNativeWindowType>(m_window);
}
#define LOAD(x) m_wayland_lib.GetSymbol(#x, &m_##x)
LOAD(wl_egl_window_create);
LOAD(wl_egl_window_destroy);
LOAD(wl_egl_window_resize);
#undef LOAD
}
if (m_window)
m_wl_egl_window_destroy(m_window);
m_window = m_wl_egl_window_create(static_cast<wl_surface*>(m_wsi.render_surface),
m_wsi.render_surface_width, m_wsi.render_surface_height);
return reinterpret_cast<EGLNativeWindowType>(m_window);
}

View File

@ -0,0 +1,28 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <wayland-egl.h>
#include "Common/DynamicLibrary.h"
#include "Common/GL/GLInterface/EGL.h"
struct wl_egl_window;
struct wl_surface;
class GLContextEGLWayland final : public GLContextEGL
{
public:
~GLContextEGLWayland() override;
void Update(u32 width, u32 height) override;
protected:
EGLDisplay OpenEGLDisplay() override;
EGLNativeWindowType GetEGLNativeWindow(EGLConfig config) override;
Common::DynamicLibrary m_wayland_lib;
wl_egl_window* (*m_wl_egl_window_create)(wl_surface* surface, int width, int height);
void (*m_wl_egl_window_destroy)(wl_egl_window* window);
void (*m_wl_egl_window_resize)(wl_egl_window* window, int width, int height, int dx, int dy);
wl_egl_window* m_window = nullptr;
};

View File

@ -11,7 +11,7 @@ GLContextEGLX11::~GLContextEGLX11()
m_render_window.reset();
}
void GLContextEGLX11::Update()
void GLContextEGLX11::Update(u32, u32)
{
m_render_window->UpdateDimensions();
m_backbuffer_width = m_render_window->GetWidth();

View File

@ -13,7 +13,7 @@ class GLContextEGLX11 final : public GLContextEGL
public:
~GLContextEGLX11() override;
void Update() override;
void Update(u32, u32) override;
protected:
EGLDisplay OpenEGLDisplay() override;

View File

@ -478,14 +478,3 @@ bool GLContextWGL::ClearCurrent()
{
return wglMakeCurrent(m_dc, nullptr) == TRUE;
}
// Update window width, size and etc. Called from Render.cpp
void GLContextWGL::Update()
{
RECT rcWindow;
GetClientRect(m_window_handle, &rcWindow);
// Get the new window width and height
m_backbuffer_width = rcWindow.right - rcWindow.left;
m_backbuffer_height = rcWindow.bottom - rcWindow.top;
}

View File

@ -19,8 +19,6 @@ public:
bool MakeCurrent() override;
bool ClearCurrent() override;
void Update() override;
void Swap() override;
void SwapInterval(int interval) override;

View File

@ -41,6 +41,11 @@ struct WindowSystemInfo
// during video backend startup the surface pointer may change (MoltenVK).
void* render_surface = nullptr;
// Width of the render surface
int render_surface_width = 0;
// Height of the render surface
int render_surface_height = 0;
// Scale of the render surface. For hidpi systems, this will be >1.
float render_surface_scale = 1.0f;
};

View File

@ -85,6 +85,9 @@ WindowSystemInfo PlatformFBDev::GetWindowSystemInfo() const
wsi.display_connection = nullptr; // EGL_DEFAULT_DISPLAY
wsi.render_window = nullptr;
wsi.render_surface = nullptr;
wsi.render_surface_width = 0;
wsi.render_surface_height = 0;
wsi.render_surface_scale = 1.f;
return wsi;
}
} // namespace

View File

@ -21,6 +21,39 @@
#include <cstring>
#include <thread>
@class AppDelegate;
class PlatformMacOS : public Platform
{
public:
~PlatformMacOS() override;
bool Init() override;
void SetTitle(const std::string& title) override;
void MainLoop() override;
WindowSystemInfo GetWindowSystemInfo() const override;
NSWindow* Window() const { return m_window; }
private:
void ProcessEvents();
void UpdateWindowPosition();
void HandleSaveStates(NSUInteger key, NSUInteger flags);
void SetupMenu();
NSRect m_window_rect;
NSWindow* m_window;
NSMenu* menuBar;
AppDelegate* m_app_delegate;
int m_window_x = Config::Get(Config::MAIN_RENDER_WINDOW_XPOS);
int m_window_y = Config::Get(Config::MAIN_RENDER_WINDOW_YPOS);
unsigned int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH);
unsigned int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT);
bool m_window_fullscreen = Config::Get(Config::MAIN_FULLSCREEN);
};
@interface Application : NSApplication
@property Platform* platform;
- (void)shutdown;
@ -80,12 +113,13 @@
}
@end
@interface AppDelegate : NSObject <NSApplicationDelegate>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
@property(readonly) Platform* platform;
@property(readonly) PlatformMacOS* platform;
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender;
- (id)initWithPlatform:(Platform*)platform;
- (void)windowDidResize:(NSNotification*)notification;
@end
@implementation AppDelegate
@ -94,7 +128,7 @@
return YES;
}
- (id)initWithPlatform:(Platform*)platform
- (id)initWithPlatform:(PlatformMacOS*)platform
{
self = [super init];
if (self)
@ -103,54 +137,25 @@
}
return self;
}
@end
@interface WindowDelegate : NSObject <NSWindowDelegate>
- (void)windowDidResize:(NSNotification*)notification;
@end
@implementation WindowDelegate
- (void)windowDidResize:(NSNotification*)notification
{
WindowSystemInfo info = _platform->GetWindowSystemInfo();
if (g_presenter)
g_presenter->ResizeSurface();
g_presenter->ResizeSurface(info.render_surface_width, info.render_surface_height,
info.render_surface_scale);
}
- (void)windowDidChangeScreen:(NSNotification*)notification
{
if (NSWindow* window = _platform->Window())
if (CALayer* layer = [[window contentView] layer])
[layer setContentsScale:[window backingScaleFactor]];
[self windowDidResize:notification];
}
@end
namespace
{
class PlatformMacOS : public Platform
{
public:
~PlatformMacOS() override;
bool Init() override;
void SetTitle(const std::string& title) override;
void MainLoop() override;
WindowSystemInfo GetWindowSystemInfo() const override;
private:
void ProcessEvents();
void UpdateWindowPosition();
void HandleSaveStates(NSUInteger key, NSUInteger flags);
void SetupMenu();
NSRect m_window_rect;
NSWindow* m_window;
NSMenu* menuBar;
AppDelegate* m_app_delegate;
WindowDelegate* m_window_delegate;
int m_window_x = Config::Get(Config::MAIN_RENDER_WINDOW_XPOS);
int m_window_y = Config::Get(Config::MAIN_RENDER_WINDOW_YPOS);
unsigned int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH);
unsigned int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT);
bool m_window_fullscreen = Config::Get(Config::MAIN_FULLSCREEN);
};
PlatformMacOS::~PlatformMacOS()
{
[m_window close];
@ -176,8 +181,7 @@ bool PlatformMacOS::Init()
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:NO];
m_window_delegate = [[WindowDelegate alloc] init];
[m_window setDelegate:m_window_delegate];
[m_window setDelegate:m_app_delegate];
NSNotificationCenter* c = [NSNotificationCenter defaultCenter];
[c addObserver:NSApp
@ -237,10 +241,15 @@ WindowSystemInfo PlatformMacOS::GetWindowSystemInfo() const
{
@autoreleasepool
{
NSRect frame = [m_window contentRectForFrameRect:[m_window frame]];
CGFloat scale = [m_window backingScaleFactor];
WindowSystemInfo wsi;
wsi.type = WindowSystemType::MacOS;
wsi.render_window = (void*)CFBridgingRetain([m_window contentView]);
wsi.render_window = (__bridge void*)[m_window contentView];
wsi.render_surface = wsi.render_window;
wsi.render_surface_width = frame.size.width * scale;
wsi.render_surface_height = frame.size.height * scale;
wsi.render_surface_scale = scale;
return wsi;
}
}
@ -421,8 +430,6 @@ void PlatformMacOS::SetupMenu()
}
}
} // namespace
std::unique_ptr<Platform> Platform::CreateMacOSPlatform()
{
return std::make_unique<PlatformMacOS>();

View File

@ -137,6 +137,9 @@ WindowSystemInfo PlatformWin32::GetWindowSystemInfo() const
wsi.type = WindowSystemType::Windows;
wsi.render_window = reinterpret_cast<void*>(m_hwnd);
wsi.render_surface = reinterpret_cast<void*>(m_hwnd);
wsi.render_surface_width = m_window_width;
wsi.render_surface_height = m_window_height;
wsi.render_surface_scale = 1.f;
return wsi;
}
@ -192,8 +195,9 @@ LRESULT PlatformWin32::WndProc(const HWND hwnd, const UINT msg, const WPARAM wPa
case WM_SIZE:
{
UpdateWindowPosition();
if (g_presenter)
g_presenter->ResizeSurface();
g_presenter->ResizeSurface(m_window_width, m_window_height, 1.f);
}
break;

View File

@ -167,6 +167,10 @@ WindowSystemInfo PlatformX11::GetWindowSystemInfo() const
wsi.display_connection = static_cast<void*>(m_display);
wsi.render_window = reinterpret_cast<void*>(m_window);
wsi.render_surface = reinterpret_cast<void*>(m_window);
// TODO: Actually get these (currently both the X11 Vulkan and OGL backends ignore this)
wsi.render_surface_width = 0;
wsi.render_surface_height = 0;
wsi.render_surface_scale = 1.f;
return wsi;
}
@ -267,7 +271,7 @@ void PlatformX11::ProcessEvents()
case ConfigureNotify:
{
if (g_presenter)
g_presenter->ResizeSurface();
g_presenter->ResizeSurface(0, 0, 1.f);
}
break;
}

View File

@ -56,21 +56,6 @@ Host* Host::GetInstance()
return s_instance;
}
void Host::SetRenderHandle(void* handle)
{
m_render_to_main = Config::Get(Config::MAIN_RENDER_TO_MAIN);
if (m_render_handle == handle)
return;
m_render_handle = handle;
if (g_presenter)
{
g_presenter->ChangeSurface(handle);
g_controller_interface.ChangeWindow(handle);
}
}
void Host::SetMainWindowHandle(void* handle)
{
m_main_window_handle = handle;
@ -185,10 +170,20 @@ void Host::SetTASInputFocus(const bool focus)
m_tas_input_focus = focus;
}
void Host::ResizeSurface(int new_width, int new_height)
void Host::SetRenderWindowInfo(void* new_handle, int new_width, int new_height, float new_scale)
{
if (new_handle)
{
m_render_to_main = Config::Get(Config::MAIN_RENDER_TO_MAIN);
if (m_render_handle == new_handle)
new_handle = nullptr;
else
m_render_handle = new_handle;
}
if (g_presenter)
g_presenter->ResizeSurface();
g_presenter->ChangeSurface(new_handle, new_width, new_height, new_scale);
if (g_presenter && new_handle)
g_controller_interface.ChangeWindow(new_handle);
}
std::vector<std::string> Host_GetPreferredLocales()

View File

@ -28,10 +28,11 @@ public:
bool GetTASInputFocus() const;
void SetMainWindowHandle(void* handle);
void SetRenderHandle(void* handle);
void SetRenderFocus(bool focus);
void SetRenderFullFocus(bool focus);
void SetRenderFullscreen(bool fullscreen);
// null new_handle => resize
void SetRenderWindowInfo(void* new_handle, int new_width, int new_height, float new_scale);
void SetTASInputFocus(bool focus);
void ResizeSurface(int new_width, int new_height);

View File

@ -149,14 +149,14 @@ int main(int argc, char* argv[])
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
setenv("QT_XCB_NO_XI2", "1", true);
#endif
// Dolphin currently doesn't work on Wayland (Only the UI does, games do not launch.) This makes
// XCB the default and forces it on if the platform is specified to be wayland, to prevent this
// from happening.
// For more information: https://bugs.dolphin-emu.org/issues/11807
#if !HAVE_WAYLAND
// If Dolphin was built without Wayland support, force XCB when the platform is explicitly set to
// Wayland so users get a working backend.
const char* current_qt_platform = getenv("QT_QPA_PLATFORM");
const bool replace_qt_platform = current_qt_platform != nullptr &&
Common::CaseInsensitiveContains(current_qt_platform, "wayland");
setenv("QT_QPA_PLATFORM", "xcb", replace_qt_platform);
#endif
#endif
QCoreApplication::setOrganizationName(QStringLiteral("Dolphin Emulator"));

View File

@ -168,7 +168,7 @@ static WindowSystemType GetWindowSystemType()
return WindowSystemType::MacOS;
else if (platform_name == QStringLiteral("xcb"))
return WindowSystemType::X11;
else if (platform_name == QStringLiteral("wayland"))
else if (platform_name.startsWith(QStringLiteral("wayland")))
return WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("haiku"))
return WindowSystemType::Haiku;
@ -197,7 +197,10 @@ static WindowSystemInfo GetWindowSystemInfo(QWindow* window)
wsi.render_window = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
wsi.render_surface = wsi.render_window;
#endif
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
float dpr = window ? static_cast<float>(window->devicePixelRatio()) : 1.f;
wsi.render_surface_width = window ? static_cast<u32>(window->width() * dpr) : 0;
wsi.render_surface_height = window ? static_cast<u32>(window->height() * dpr) : 0;
wsi.render_surface_scale = dpr;
return wsi;
}

View File

@ -66,9 +66,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
// (which results in them not getting called)
connect(this, &RenderWidget::StateChanged, Host::GetInstance(), &Host::SetRenderFullscreen,
Qt::DirectConnection);
connect(this, &RenderWidget::HandleChanged, this, &RenderWidget::OnHandleChanged,
Qt::DirectConnection);
connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::ResizeSurface,
connect(this, &RenderWidget::WindowChanged, Host::GetInstance(), &Host::SetRenderWindowInfo,
Qt::DirectConnection);
connect(this, &RenderWidget::FocusChanged, Host::GetInstance(), &Host::SetRenderFocus,
Qt::DirectConnection);
@ -130,20 +128,6 @@ void RenderWidget::dropEvent(QDropEvent* event)
State::LoadAs(Core::System::GetInstance(), path.toStdString());
}
void RenderWidget::OnHandleChanged(void* handle)
{
if (handle)
{
#ifdef _WIN32
// Remove rounded corners from the render window on Windows 11
const DWM_WINDOW_CORNER_PREFERENCE corner_preference = DWMWCP_DONOTROUND;
DwmSetWindowAttribute(static_cast<HWND>(handle), DWMWA_WINDOW_CORNER_PREFERENCE,
&corner_preference, sizeof(corner_preference));
#endif
}
Host::GetInstance()->SetRenderHandle(handle);
}
void RenderWidget::OnHideCursorChanged()
{
UpdateCursor();
@ -212,7 +196,7 @@ void RenderWidget::showFullScreen()
const auto dpr = screen->devicePixelRatio();
emit SizeChanged(width() * dpr, height() * dpr);
emit WindowChanged(nullptr, width() * dpr, height() * dpr, dpr);
}
// Lock the cursor within the window/widget internal borders, including the aspect ratio if wanted
@ -402,9 +386,6 @@ bool RenderWidget::event(QEvent* event)
m_mouse_timer->start(MOUSE_HIDE_DELAY);
}
break;
case QEvent::WinIdChange:
emit HandleChanged(reinterpret_cast<void*>(winId()));
break;
case QEvent::Show:
// Don't do if "stay on top" changed (or was true)
if (Settings::Instance().GetLockCursor() &&
@ -469,27 +450,37 @@ bool RenderWidget::event(QEvent* event)
// According to https://bugreports.qt.io/browse/QTBUG-95925 the recommended practice for
// handling DPI change is responding to paint events
case QEvent::WinIdChange:
case QEvent::Paint:
case QEvent::Resize:
{
SetCursorLocked(m_cursor_locked);
void* handle = nullptr;
if (event->type() == QEvent::WinIdChange)
{
handle = reinterpret_cast<void*>(winId());
#ifdef _WIN32
// Remove rounded corners from the render window on Windows 11
const DWM_WINDOW_CORNER_PREFERENCE corner_preference = DWMWCP_DONOTROUND;
DwmSetWindowAttribute(reinterpret_cast<HWND>(handle), DWMWA_WINDOW_CORNER_PREFERENCE,
&corner_preference, sizeof(corner_preference));
#endif
}
const QResizeEvent* se = static_cast<QResizeEvent*>(event);
QSize new_size = se->size();
SetCursorLocked(m_cursor_locked);
QScreen* screen = window()->windowHandle()->screen();
const float dpr = screen->devicePixelRatio();
const int width = new_size.width() * dpr;
const int height = new_size.height() * dpr;
const int width = this->width() * dpr;
const int height = this->height() * dpr;
if (m_last_window_width != width || m_last_window_height != height ||
if (handle || m_last_window_width != width || m_last_window_height != height ||
m_last_window_scale != dpr)
{
m_last_window_width = width;
m_last_window_height = height;
m_last_window_scale = dpr;
emit SizeChanged(width, height);
emit WindowChanged(handle, width, height, dpr);
}
break;
}

View File

@ -27,14 +27,13 @@ public:
signals:
void EscapePressed();
void Closed();
void HandleChanged(void* handle);
void StateChanged(bool fullscreen);
void SizeChanged(int new_width, int new_height);
// null new_handle => resize only
void WindowChanged(void* new_handle, int new_width, int new_height, float new_scale);
void FocusChanged(bool focus);
private:
void HandleCursorTimer();
void OnHandleChanged(void* handle);
void OnHideCursorChanged();
void OnNeverHideCursorChanged();
void OnLockCursorChanged();

View File

@ -184,22 +184,25 @@ void Gfx::OnConfigChanged(u32 bits)
void Gfx::CheckForSwapChainChanges()
{
const bool surface_changed = g_presenter->SurfaceChangedTestAndClear();
const auto change_info = g_presenter->SurfaceChangedTestAndClear();
const bool surface_resized =
g_presenter->SurfaceResizedTestAndClear() || m_swap_chain->CheckForFullscreenChange();
if (!surface_changed && !surface_resized)
(change_info && !change_info->new_handle) || m_swap_chain->CheckForFullscreenChange();
if (!change_info && !surface_resized)
return;
if (surface_changed)
if (change_info && change_info->new_handle)
{
m_swap_chain->ChangeSurface(g_presenter->GetNewSurfaceHandle());
m_swap_chain->ChangeSurface(change_info->new_handle);
}
else
{
m_swap_chain->ResizeSwapChain();
}
g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
if (change_info)
m_backbuffer_scale = change_info->new_scale;
g_presenter->SetBackbuffer(GetSurfaceInfo());
}
void Gfx::SetFramebuffer(AbstractFramebuffer* framebuffer)

View File

@ -374,24 +374,27 @@ bool Gfx::BindBackbuffer(const ClearColor& clear_color)
void Gfx::CheckForSwapChainChanges()
{
const bool surface_changed = g_presenter->SurfaceChangedTestAndClear();
const auto change_info = g_presenter->SurfaceChangedTestAndClear();
const bool surface_resized =
g_presenter->SurfaceResizedTestAndClear() || m_swap_chain->CheckForFullscreenChange();
if (!surface_changed && !surface_resized)
(change_info && !change_info->new_handle) || m_swap_chain->CheckForFullscreenChange();
if (!change_info && !surface_resized)
return;
// The swap chain could be in use from a previous frame.
WaitForGPUIdle();
if (surface_changed)
if (change_info && change_info->new_handle)
{
m_swap_chain->ChangeSurface(g_presenter->GetNewSurfaceHandle());
m_swap_chain->ChangeSurface(change_info->new_handle);
}
else
{
m_swap_chain->ResizeSwapChain();
}
g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
if (change_info)
m_backbuffer_scale = change_info->new_scale;
g_presenter->SetBackbuffer(GetSurfaceInfo());
}
void Gfx::PresentBackbuffer()

View File

@ -10,6 +10,8 @@
#include "VideoBackends/Metal/MRCHelpers.h"
struct WindowSystemInfo;
namespace Metal
{
class Framebuffer;
@ -18,7 +20,7 @@ class Texture;
class Gfx final : public ::AbstractGfx
{
public:
Gfx(MRCOwned<CAMetalLayer*> layer);
Gfx(MRCOwned<CAMetalLayer*> layer, const WindowSystemInfo& wsi);
~Gfx() override;
bool IsHeadless() const override;
@ -84,7 +86,6 @@ private:
std::array<u32, 4> m_shader_counter = {};
void CheckForSurfaceChange();
void CheckForSurfaceResize();
void SetupSurface();
void SetupSurface(u32 width, u32 height, float scale);
};
} // namespace Metal

View File

@ -18,12 +18,13 @@
#include <fstream>
Metal::Gfx::Gfx(MRCOwned<CAMetalLayer*> layer) : m_layer(std::move(layer))
Metal::Gfx::Gfx(MRCOwned<CAMetalLayer*> layer, const WindowSystemInfo& wsi)
: m_layer(std::move(layer))
{
UpdateActiveConfig();
[m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive];
SetupSurface();
SetupSurface(wsi.render_surface_width, wsi.render_surface_height, wsi.render_surface_scale);
g_state_tracker->FlushEncoders();
}
@ -453,7 +454,6 @@ bool Metal::Gfx::BindBackbuffer(const ClearColor& clear_color)
@autoreleasepool
{
CheckForSurfaceChange();
CheckForSurfaceResize();
m_drawable = MRCRetain([m_layer nextDrawable]);
m_backbuffer->UpdateBackbufferTexture([m_drawable texture]);
SetAndClearFramebuffer(m_backbuffer.get(), clear_color);
@ -488,33 +488,28 @@ void Metal::Gfx::PresentBackbuffer()
void Metal::Gfx::CheckForSurfaceChange()
{
if (!g_presenter->SurfaceChangedTestAndClear())
return;
m_layer = MRCRetain(static_cast<CAMetalLayer*>(g_presenter->GetNewSurfaceHandle()));
SetupSurface();
if (auto change_info = g_presenter->SurfaceChangedTestAndClear())
{
if (void* handle = change_info->new_handle)
m_layer = MRCRetain(static_cast<CAMetalLayer*>(handle));
SetupSurface(change_info->new_width, change_info->new_height, change_info->new_scale);
}
}
void Metal::Gfx::CheckForSurfaceResize()
void Metal::Gfx::SetupSurface(u32 width, u32 height, float scale)
{
if (!g_presenter->SurfaceResizedTestAndClear())
return;
SetupSurface();
}
AbstractTextureFormat format = Util::ToAbstract([m_layer pixelFormat]);
[m_layer setContentsScale:scale];
[m_layer setDrawableSize:{static_cast<double>(width), static_cast<double>(height)}];
void Metal::Gfx::SetupSurface()
{
auto info = GetSurfaceInfo();
[m_layer setDrawableSize:{static_cast<double>(info.width), static_cast<double>(info.height)}];
TextureConfig cfg(info.width, info.height, 1, 1, 1, info.format, AbstractTextureFlag_RenderTarget,
TextureConfig cfg(width, height, 1, 1, 1, format, AbstractTextureFlag_RenderTarget,
AbstractTextureType::Texture_2DArray);
m_bb_texture = std::make_unique<Texture>(nullptr, cfg);
m_backbuffer = std::make_unique<Framebuffer>(
m_bb_texture.get(), nullptr, std::vector<AbstractTexture*>{}, info.width, info.height, 1, 1);
m_bb_texture.get(), nullptr, std::vector<AbstractTexture*>{}, width, height, 1, 1);
if (g_presenter)
g_presenter->SetBackbuffer(info);
g_presenter->SetBackbuffer({width, height, scale, format});
}
SurfaceInfo Metal::Gfx::GetSurfaceInfo() const
@ -522,9 +517,9 @@ SurfaceInfo Metal::Gfx::GetSurfaceInfo() const
if (!m_layer) // Headless
return {};
CGSize size = [m_layer bounds].size;
CGSize size = [m_layer drawableSize];
const float scale = [m_layer contentsScale];
return {static_cast<u32>(size.width * scale), static_cast<u32>(size.height * scale), scale,
return {static_cast<u32>(size.width), static_cast<u32>(size.height), scale,
Util::ToAbstract([m_layer pixelFormat])};
}

View File

@ -125,9 +125,9 @@ bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi)
ObjectCache::Initialize(std::move(adapter));
g_state_tracker = std::make_unique<StateTracker>();
return InitializeShared(
std::make_unique<Metal::Gfx>(std::move(layer)), std::make_unique<Metal::VertexManager>(),
std::make_unique<Metal::PerfQuery>(), std::make_unique<Metal::BoundingBox>());
return InitializeShared(std::make_unique<Gfx>(std::move(layer), wsi),
std::make_unique<VertexManager>(), std::make_unique<PerfQuery>(),
std::make_unique<BoundingBox>());
}
}

View File

@ -410,7 +410,6 @@ void OGLGfx::ClearRegion(const MathUtil::Rectangle<int>& target_rc, bool colorEn
bool OGLGfx::BindBackbuffer(const ClearColor& clear_color)
{
CheckForSurfaceChange();
CheckForSurfaceResize();
SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color);
return true;
}
@ -459,29 +458,21 @@ void OGLGfx::WaitForGPUIdle()
void OGLGfx::CheckForSurfaceChange()
{
if (!g_presenter->SurfaceChangedTestAndClear())
return;
if (auto change_info = g_presenter->SurfaceChangedTestAndClear())
{
if (void* handle = change_info->new_handle)
m_main_gl_context->UpdateSurface(handle, change_info->new_width, change_info->new_height);
else
m_main_gl_context->Update(change_info->new_width, change_info->new_height);
m_main_gl_context->UpdateSurface(g_presenter->GetNewSurfaceHandle());
u32 width = m_main_gl_context->GetBackBufferWidth();
u32 height = m_main_gl_context->GetBackBufferHeight();
m_backbuffer_scale = change_info->new_scale;
u32 width = m_main_gl_context->GetBackBufferWidth();
u32 height = m_main_gl_context->GetBackBufferHeight();
// With a surface change, the window likely has new dimensions.
g_presenter->SetBackbuffer(width, height);
m_system_framebuffer->UpdateDimensions(width, height);
}
void OGLGfx::CheckForSurfaceResize()
{
if (!g_presenter->SurfaceResizedTestAndClear())
return;
m_main_gl_context->Update();
u32 width = m_main_gl_context->GetBackBufferWidth();
u32 height = m_main_gl_context->GetBackBufferHeight();
g_presenter->SetBackbuffer(width, height);
m_system_framebuffer->UpdateDimensions(width, height);
// With a surface change, the window likely has new dimensions.
g_presenter->SetBackbuffer(GetSurfaceInfo());
m_system_framebuffer->UpdateDimensions(width, height);
}
}
void OGLGfx::BeginUtilityDrawing()

View File

@ -96,7 +96,6 @@ public:
private:
void CheckForSurfaceChange();
void CheckForSurfaceResize();
void ApplyRasterizationState(const RasterizationState state);
void ApplyDepthState(const DepthState state);

View File

@ -58,12 +58,12 @@ SWGfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* dep
bool SWGfx::BindBackbuffer(const ClearColor& clear_color)
{
// Look for framebuffer resizes
if (!g_presenter->SurfaceResizedTestAndClear())
return true;
GLContext* context = m_window->GetContext();
context->Update();
g_presenter->SetBackbuffer(context->GetBackBufferWidth(), context->GetBackBufferHeight());
if (auto change_info = g_presenter->SurfaceChangedTestAndClear())
{
GLContext* context = m_window->GetContext();
context->Update(change_info->new_width, change_info->new_height);
g_presenter->SetBackbuffer(GetSurfaceInfo());
}
return true;
}

View File

@ -88,7 +88,6 @@ void SWOGLWindow::ShowImage(const AbstractTexture* image,
const MathUtil::Rectangle<int>& xfb_region)
{
const SW::SWTexture* sw_image = static_cast<const SW::SWTexture*>(image);
m_gl_context->Update(); // just updates the render window position and the backbuffer size
GLsizei glWidth = (GLsizei)m_gl_context->GetBackBufferWidth();
GLsizei glHeight = (GLsizei)m_gl_context->GetBackBufferHeight();

View File

@ -225,7 +225,6 @@ bool VKGfx::BindBackbuffer(const ClearColor& clear_color)
// Handle host window resizes.
CheckForSurfaceChange();
CheckForSurfaceResize();
// Check for exclusive fullscreen request.
if (m_swap_chain->GetCurrentFullscreenState() != m_swap_chain->GetNextFullscreenState() &&
@ -263,18 +262,18 @@ bool VKGfx::BindBackbuffer(const ClearColor& clear_color)
{
// The present keeps returning exclusive mode lost unless we re-create the swap chain.
INFO_LOG_FMT(VIDEO, "Lost exclusive fullscreen.");
m_swap_chain->RecreateSwapChain();
m_swap_chain->RecreateSwapChain(0, 0);
}
else if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
{
INFO_LOG_FMT(VIDEO, "Resizing swap chain due to suboptimal/out-of-date");
m_swap_chain->ResizeSwapChain();
m_swap_chain->ResizeSwapChain(0, 0);
}
else
{
ERROR_LOG_FMT(VIDEO, "Unknown present error {:#010X} {}, please report.",
std::to_underlying(res), VkResultToString(res));
m_swap_chain->RecreateSwapChain();
m_swap_chain->RecreateSwapChain(0, 0);
}
res = m_swap_chain->AcquireNextImage();
@ -358,7 +357,8 @@ void VKGfx::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completio
void VKGfx::CheckForSurfaceChange()
{
if (!g_presenter->SurfaceChangedTestAndClear() || !m_swap_chain)
std::optional<VideoCommon::Presenter::SurfaceChangedInfo> change_info;
if (!(change_info = g_presenter->SurfaceChangedTestAndClear()) || !m_swap_chain)
return;
// Submit the current draws up until rendering the XFB.
@ -367,38 +367,25 @@ void VKGfx::CheckForSurfaceChange()
// Clear the present failed flag, since we don't want to resize after recreating.
g_command_buffer_mgr->CheckLastPresentFail();
// Recreate the surface. If this fails we're in trouble.
if (!m_swap_chain->RecreateSurface(g_presenter->GetNewSurfaceHandle()))
PanicAlertFmt("Failed to recreate Vulkan surface. Cannot continue.");
if (change_info->new_handle)
{
// Recreate the surface. If this fails we're in trouble.
if (!m_swap_chain->RecreateSurface(change_info->new_handle, //
change_info->new_width, change_info->new_height))
{
PanicAlertFmt("Failed to recreate Vulkan surface. Cannot continue.");
}
}
else
{
m_swap_chain->RecreateSwapChain(change_info->new_width, change_info->new_height);
}
m_backbuffer_scale = change_info->new_scale;
// Handle case where the dimensions are now different.
OnSwapChainResized();
}
void VKGfx::CheckForSurfaceResize()
{
if (!g_presenter->SurfaceResizedTestAndClear())
return;
// If we don't have a surface, how can we resize the swap chain?
// CheckForSurfaceChange should handle this case.
if (!m_swap_chain)
{
WARN_LOG_FMT(VIDEO, "Surface resize event received without active surface, ignoring");
return;
}
// Wait for the GPU to catch up since we're going to destroy the swap chain.
ExecuteCommandBuffer(false, true);
// Clear the present failed flag, since we don't want to resize after recreating.
g_command_buffer_mgr->CheckLastPresentFail();
// Resize the swap chain.
m_swap_chain->RecreateSwapChain();
OnSwapChainResized();
}
void VKGfx::OnConfigChanged(u32 bits)
{
AbstractGfx::OnConfigChanged(bits);
@ -417,7 +404,7 @@ void VKGfx::OnConfigChanged(u32 bits)
if (m_swap_chain && ((bits & CONFIG_CHANGE_BIT_STEREO_MODE) || (bits & CONFIG_CHANGE_BIT_HDR)))
{
ExecuteCommandBuffer(false, true);
m_swap_chain->RecreateSwapChain();
m_swap_chain->RecreateSwapChain(0, 0);
}
// Wipe sampler cache if force texture filtering or anisotropy changes.
@ -430,7 +417,7 @@ void VKGfx::OnConfigChanged(u32 bits)
void VKGfx::OnSwapChainResized()
{
g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight());
g_presenter->SetBackbuffer(GetSurfaceInfo());
}
void VKGfx::BindFramebuffer(VKFramebuffer* fb)

View File

@ -89,7 +89,6 @@ public:
private:
void CheckForSurfaceChange();
void CheckForSurfaceResize();
void ResetSamplerStates();

View File

@ -84,6 +84,29 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowSys
}
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
if (wsi.type == WindowSystemType::Wayland)
{
VkWaylandSurfaceCreateInfoKHR surface_create_info = {
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, // VkStructureType sType
nullptr, // const void* pNext
0, // VkWaylandSurfaceCreateFlagsKHR flags
static_cast<wl_display*>(wsi.display_connection), // struct wl_display* display
static_cast<wl_surface*>(wsi.render_surface) // struct wl_surface* surface
};
VkSurfaceKHR surface;
VkResult res = vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateWaylandSurfaceKHR failed: ");
return VK_NULL_HANDLE;
}
return surface;
}
#endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
if (wsi.type == WindowSystemType::Android)
{
@ -132,8 +155,11 @@ std::unique_ptr<SwapChain> SwapChain::Create(const WindowSystemInfo& wsi, VkSurf
bool vsync)
{
std::unique_ptr<SwapChain> swap_chain = std::make_unique<SwapChain>(wsi, surface, vsync);
if (!swap_chain->CreateSwapChain() || !swap_chain->SetupSwapChainImages())
if (!swap_chain->CreateSwapChain(wsi.render_surface_width, wsi.render_surface_height) ||
!swap_chain->SetupSwapChainImages())
{
return nullptr;
}
return swap_chain;
}
@ -271,7 +297,7 @@ bool SwapChain::SelectPresentMode()
return true;
}
bool SwapChain::CreateSwapChain()
bool SwapChain::CreateSwapChain(u32 width, u32 height)
{
// Look up surface properties to determine image count and dimensions
VkSurfaceCapabilitiesKHR surface_capabilities;
@ -299,8 +325,8 @@ bool SwapChain::CreateSwapChain()
VkExtent2D size = surface_capabilities.currentExtent;
if (size.width == UINT32_MAX)
{
size.width = std::max(g_presenter->GetBackbufferWidth(), 1);
size.height = std::max(g_presenter->GetBackbufferHeight(), 1);
size.width = width == 0 ? m_width : width;
size.height = height == 0 ? m_height : height;
}
size.width = std::clamp(size.width, surface_capabilities.minImageExtent.width,
surface_capabilities.maxImageExtent.width);
@ -500,10 +526,10 @@ VkResult SwapChain::AcquireNextImage()
return res;
}
bool SwapChain::ResizeSwapChain()
bool SwapChain::ResizeSwapChain(u32 width, u32 height)
{
DestroySwapChainImages();
if (!CreateSwapChain() || !SetupSwapChainImages())
if (!CreateSwapChain(width, height) || !SetupSwapChainImages())
{
PanicAlertFmt("Failed to re-configure swap chain images, this is fatal (for now)");
return false;
@ -512,11 +538,11 @@ bool SwapChain::ResizeSwapChain()
return true;
}
bool SwapChain::RecreateSwapChain()
bool SwapChain::RecreateSwapChain(u32 width, u32 height)
{
DestroySwapChainImages();
DestroySwapChain();
if (!CreateSwapChain() || !SetupSwapChainImages())
if (!CreateSwapChain(width, height) || !SetupSwapChainImages())
{
PanicAlertFmt("Failed to re-configure swap chain images, this is fatal (for now)");
return false;
@ -532,7 +558,7 @@ bool SwapChain::SetVSync(bool enabled)
// Recreate the swap chain with the new present mode.
m_vsync_enabled = enabled;
return RecreateSwapChain();
return RecreateSwapChain(m_width, m_height);
}
bool SwapChain::SetFullscreenState(bool state)
@ -568,7 +594,7 @@ bool SwapChain::SetFullscreenState(bool state)
#endif
}
bool SwapChain::RecreateSurface(void* native_handle)
bool SwapChain::RecreateSurface(void* native_handle, u32 width, u32 height)
{
// Destroy the old swap chain, images, and surface.
DestroySwapChainImages();
@ -604,7 +630,7 @@ bool SwapChain::RecreateSurface(void* native_handle)
m_next_fullscreen_state = false;
// Finally re-create the swap chain
if (!CreateSwapChain() || !SetupSwapChainImages())
if (!CreateSwapChain(width, height) || !SetupSwapChainImages())
return false;
return true;

View File

@ -54,9 +54,9 @@ public:
}
VkResult AcquireNextImage();
bool RecreateSurface(void* native_handle);
bool ResizeSwapChain();
bool RecreateSwapChain();
bool RecreateSurface(void* native_handle, u32 width, u32 height);
bool ResizeSwapChain(u32 width, u32 height);
bool RecreateSwapChain(u32 width, u32 height);
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
bool SetVSync(bool enabled);
@ -76,7 +76,7 @@ private:
bool SelectSurfaceFormat();
bool SelectPresentMode();
bool CreateSwapChain();
bool CreateSwapChain(u32 width, u32 height);
void DestroySwapChain();
bool SetupSwapChainImages();

View File

@ -362,6 +362,13 @@ bool VulkanContext::SelectInstanceExtensions(std::vector<const char*>* extension
return false;
}
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
if (wstype == WindowSystemType::Wayland &&
!AddExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true))
{
return false;
}
#endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
if (wstype == WindowSystemType::Android &&
!AddExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))

View File

@ -49,6 +49,10 @@ VULKAN_INSTANCE_ENTRY_POINT(vkCreateXlibSurfaceKHR, false)
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXlibPresentationSupportKHR, false)
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
VULKAN_INSTANCE_ENTRY_POINT(vkCreateWaylandSurfaceKHR, false)
#endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false)
#endif

View File

@ -13,6 +13,10 @@
#define VK_USE_PLATFORM_XLIB_KHR
#endif
#if defined(HAVE_WAYLAND)
#define VK_USE_PLATFORM_WAYLAND_KHR
#endif
#if defined(ANDROID)
#define VK_USE_PLATFORM_ANDROID_KHR
#endif

View File

@ -583,24 +583,33 @@ void Presenter::ReleaseXFBContentLock()
m_xfb_entry->ReleaseContentLock();
}
void Presenter::ChangeSurface(void* new_surface_handle)
void Presenter::ChangeSurface(void* new_surface_handle, u32 width, u32 height, float scale)
{
std::lock_guard<std::mutex> lock(m_swap_mutex);
m_new_surface_handle = new_surface_handle;
m_surface_changed.Set();
// If you ChangeSurface then ResizeSurface, don't forget about the surface change
// (null new_surface_handle => ResizeSurface)
if (new_surface_handle)
m_new_surface_handle = new_surface_handle;
m_new_surface_width = width;
m_new_surface_height = height;
m_new_surface_scale = scale;
// Actual data is accessed under the mutex, so this can be relaxed
m_surface_changed.store(true, std::memory_order_relaxed);
}
void Presenter::ResizeSurface()
Presenter::SurfaceChangedInfo Presenter::SurfaceChangedGetAndClear()
{
std::lock_guard<std::mutex> lock(m_swap_mutex);
m_surface_resized.Set();
}
void* Presenter::GetNewSurfaceHandle()
{
void* handle = m_new_surface_handle;
SurfaceChangedInfo info = {};
// clang-format off
info.new_handle = m_new_surface_handle;
info.new_width = m_new_surface_width;
info.new_height = m_new_surface_height;
info.new_scale = m_new_surface_scale;
// clang-format on
m_new_surface_handle = nullptr;
return handle;
m_surface_changed.store(false, std::memory_order_relaxed);
return info;
}
u32 Presenter::AutoIntegralScale() const

View File

@ -14,6 +14,8 @@
#include <array>
#include <memory>
#include <mutex>
#include <optional>
#include <span>
#include <tuple>
class AbstractTexture;
@ -91,11 +93,24 @@ public:
VideoCommon::PostProcessing* GetPostProcessor() const { return m_post_processor.get(); }
// Final surface changing
// This is called when the surface is resized (WX) or the window changes (Android).
void ChangeSurface(void* new_surface_handle);
void ResizeSurface();
bool SurfaceResizedTestAndClear() { return m_surface_resized.TestAndClear(); }
bool SurfaceChangedTestAndClear() { return m_surface_changed.TestAndClear(); }
void* GetNewSurfaceHandle();
void ChangeSurface(void* new_surface_handle, u32 width, u32 height, float scale);
void ResizeSurface(u32 width, u32 height, float scale)
{
ChangeSurface(nullptr, width, height, scale);
}
struct SurfaceChangedInfo
{
void* new_handle; ///< New surface handle if changed, else null
int new_width; ///< New surface width
int new_height; ///< New surface height
float new_scale; ///< New surface scale
};
std::optional<SurfaceChangedInfo> SurfaceChangedTestAndClear()
{
if (!m_surface_changed.load(std::memory_order_relaxed))
return std::nullopt;
return SurfaceChangedGetAndClear();
}
void SetKeyMap(const DolphinKeyMap& key_map);
@ -141,8 +156,12 @@ private:
AbstractTextureFormat m_backbuffer_format = AbstractTextureFormat::Undefined;
void* m_new_surface_handle = nullptr;
Common::Flag m_surface_changed;
Common::Flag m_surface_resized;
int m_new_surface_width = 0;
int m_new_surface_height = 0;
float m_new_surface_scale = 1.f;
// note: Atomic for anti-tearing / anti-compiler-optimization only, write only under m_swap_mutex
std::atomic_bool m_surface_changed;
SurfaceChangedInfo SurfaceChangedGetAndClear();
// The presentation rectangle.
// Width and height correspond to the final output resolution.