Skip to content

Commit

Permalink
OpenGLDevice: Support both XCB and Xlib
Browse files Browse the repository at this point in the history
Required for NVIDIA+XWayland.
  • Loading branch information
stenzek committed Nov 11, 2024
1 parent 816ef45 commit e69f0d3
Show file tree
Hide file tree
Showing 27 changed files with 706 additions and 277 deletions.
4 changes: 2 additions & 2 deletions CMakeModules/DuckStationDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ endif()

if(ENABLE_X11)
find_package(X11 REQUIRED)
if (NOT X11_Xrandr_FOUND)
message(FATAL_ERROR "XRandR extension is required")
if (NOT X11_xcb_FOUND OR NOT X11_xcb_randr_FOUND OR NOT X11_X11_xcb_FOUND)
message(FATAL_ERROR "XCB, XCB-randr and X11-xcb are required")
endif()
endif()

Expand Down
4 changes: 2 additions & 2 deletions src/duckstation-qt/displaywidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ int DisplayWidget::scaledWindowHeight() const
static_cast<int>(std::ceil(static_cast<qreal>(height()) * QtUtils::GetDevicePixelRatioForWidget(this))), 1);
}

std::optional<WindowInfo> DisplayWidget::getWindowInfo(Error* error)
std::optional<WindowInfo> DisplayWidget::getWindowInfo(RenderAPI render_api, Error* error)
{
std::optional<WindowInfo> ret(QtUtils::GetWindowInfoForWidget(this, error));
std::optional<WindowInfo> ret = QtUtils::GetWindowInfoForWidget(this, render_api, error);
if (ret.has_value())
{
m_last_window_width = ret->surface_width;
Expand Down
4 changes: 3 additions & 1 deletion src/duckstation-qt/displaywidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

class Error;

enum class RenderAPI : u8;

class QCloseEvent;

class DisplayWidget final : public QWidget
Expand All @@ -28,7 +30,7 @@ class DisplayWidget final : public QWidget
int scaledWindowWidth() const;
int scaledWindowHeight() const;

std::optional<WindowInfo> getWindowInfo(Error* error);
std::optional<WindowInfo> getWindowInfo(RenderAPI render_api, Error* error);

void updateRelativeMode(bool enabled);
void updateCursor(bool hidden);
Expand Down
16 changes: 8 additions & 8 deletions src/duckstation-qt/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr

#endif

std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool fullscreen, bool render_to_main, bool surfaceless,
bool use_main_window_pos, Error* error)
std::optional<WindowInfo> MainWindow::acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos, Error* error)
{
DEV_LOG("acquireRenderWindow() fullscreen={} render_to_main={} surfaceless={} use_main_window_pos={}",
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false",
Expand Down Expand Up @@ -265,7 +265,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool fullscreen, bool
updateWindowState();

QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return m_display_widget->getWindowInfo(error);
return m_display_widget->getWindowInfo(render_api, error);
}

destroyDisplayWidget(surfaceless);
Expand All @@ -277,7 +277,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool fullscreen, bool

createDisplayWidget(fullscreen, render_to_main, use_main_window_pos);

std::optional<WindowInfo> wi = m_display_widget->getWindowInfo(error);
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo(render_api, error);
if (!wi.has_value())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
Expand Down Expand Up @@ -2483,9 +2483,9 @@ void MainWindow::checkForSettingChanges()
std::optional<WindowInfo> MainWindow::getWindowInfo()
{
if (!m_display_widget || isRenderingToMain())
return QtUtils::GetWindowInfoForWidget(this);
return QtUtils::GetWindowInfoForWidget(this, RenderAPI::None);
else if (QWidget* widget = getDisplayContainer())
return QtUtils::GetWindowInfoForWidget(widget);
return QtUtils::GetWindowInfoForWidget(widget, RenderAPI::None);
else
return std::nullopt;
}
Expand Down Expand Up @@ -2565,15 +2565,15 @@ void MainWindow::onAchievementsChallengeModeChanged(bool enabled)
updateEmulationActions(false, System::IsValid(), enabled);
}

bool MainWindow::onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
bool MainWindow::onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
{
AuxiliaryDisplayWidget* widget = AuxiliaryDisplayWidget::create(x, y, width, height, title, icon_name, userdata);
if (!widget)
return false;

const std::optional<WindowInfo> owi = QtUtils::GetWindowInfoForWidget(widget, error);
const std::optional<WindowInfo> owi = QtUtils::GetWindowInfoForWidget(widget, render_api, error);
if (!owi.has_value())
{
widget->destroy();
Expand Down
10 changes: 6 additions & 4 deletions src/duckstation-qt/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class MemoryScannerWindow;

struct SystemBootParameters;

enum class RenderAPI : u8;
class GPUDevice;
namespace Achievements {
enum class LoginRequestReason;
Expand Down Expand Up @@ -127,8 +128,8 @@ private Q_SLOTS:
bool confirmMessage(const QString& title, const QString& message);
void onStatusMessage(const QString& message);

std::optional<WindowInfo> acquireRenderWindow(bool fullscreen, bool render_to_main, bool surfaceless,
bool use_main_window_pos, Error* error);
std::optional<WindowInfo> acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos, Error* error);
void displayResizeRequested(qint32 width, qint32 height);
void releaseRenderWindow();
void focusDisplayWidget();
Expand All @@ -145,8 +146,9 @@ private Q_SLOTS:
void onMediaCaptureStopped();
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
void onAchievementsChallengeModeChanged(bool enabled);
bool onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
bool onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height,
const QString& title, const QString& icon_name,
Host::AuxiliaryRenderWindowUserData userdata,
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);

Expand Down
16 changes: 9 additions & 7 deletions src/duckstation-qt/qthost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ void QtHost::RegisterTypes()
qRegisterMetaType<std::function<void()>>("std::function<void()>");
qRegisterMetaType<std::shared_ptr<SystemBootParameters>>();
qRegisterMetaType<const GameList::Entry*>();
qRegisterMetaType<RenderAPI>("RenderAPI");
qRegisterMetaType<GPURenderer>("GPURenderer");
qRegisterMetaType<InputBindingKey>("InputBindingKey");
qRegisterMetaType<std::string>("std::string");
Expand Down Expand Up @@ -954,7 +955,8 @@ void EmuThread::requestDisplaySize(float scale)
System::RequestDisplaySize(scale);
}

std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool fullscreen, bool exclusive_fullscreen, Error* error)
std::optional<WindowInfo> EmuThread::acquireRenderWindow(RenderAPI render_api, bool fullscreen,
bool exclusive_fullscreen, Error* error)
{
DebugAssert(g_gpu_device);

Expand All @@ -964,8 +966,8 @@ std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool fullscreen, bool e
const bool render_to_main = !fullscreen && m_is_rendering_to_main;
const bool use_main_window_pos = shouldRenderToMain();

return emit onAcquireRenderWindowRequested(window_fullscreen, render_to_main, m_is_surfaceless, use_main_window_pos,
error);
return emit onAcquireRenderWindowRequested(render_api, window_fullscreen, render_to_main, m_is_surfaceless,
use_main_window_pos, error);
}

void EmuThread::releaseRenderWindow()
Expand Down Expand Up @@ -1655,9 +1657,9 @@ bool Host::CreateAuxiliaryRenderWindow(s32 x, s32 y, u32 width, u32 height, std:
std::string_view icon_name, AuxiliaryRenderWindowUserData userdata,
AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
{
return emit g_emu_thread->onCreateAuxiliaryRenderWindow(x, y, width, height, QtUtils::StringViewToQString(title),
QtUtils::StringViewToQString(icon_name), userdata, handle, wi,
error);
return emit g_emu_thread->onCreateAuxiliaryRenderWindow(
g_gpu_device->GetRenderAPI(), x, y, width, height, QtUtils::StringViewToQString(title),
QtUtils::StringViewToQString(icon_name), userdata, handle, wi, error);
}

void Host::DestroyAuxiliaryRenderWindow(AuxiliaryRenderWindowHandle handle, s32* pos_x, s32* pos_y, u32* width,
Expand Down Expand Up @@ -2002,7 +2004,7 @@ void Host::CommitBaseSettingChanges()
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
Error* error)
{
return g_emu_thread->acquireRenderWindow(fullscreen, exclusive_fullscreen, error);
return g_emu_thread->acquireRenderWindow(render_api, fullscreen, exclusive_fullscreen, error);
}

void Host::ReleaseRenderWindow()
Expand Down
13 changes: 8 additions & 5 deletions src/duckstation-qt/qthost.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class QTranslator;

class INISettingsInterface;

enum class RenderAPI : u8;
class GPUDevice;

class MainWindow;
Expand Down Expand Up @@ -94,7 +95,8 @@ class EmuThread : public QThread
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }

std::optional<WindowInfo> acquireRenderWindow(bool fullscreen, bool exclusive_fullscreen, Error* error);
std::optional<WindowInfo> acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
Error* error);
void connectDisplaySignals(DisplayWidget* widget);
void releaseRenderWindow();

Expand Down Expand Up @@ -137,8 +139,8 @@ class EmuThread : public QThread
void systemPaused();
void systemResumed();
void gameListRefreshed();
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool fullscreen, bool render_to_main, bool surfaceless,
bool use_main_window_pos, Error* error);
std::optional<WindowInfo> onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos, Error* error);
void onResizeRenderWindowRequested(qint32 width, qint32 height);
void onReleaseRenderWindowRequested();
void focusDisplayWidgetRequested();
Expand All @@ -153,8 +155,9 @@ class EmuThread : public QThread
void mediaCaptureStarted();
void mediaCaptureStopped();

bool onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
bool onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height,
const QString& title, const QString& icon_name,
Host::AuxiliaryRenderWindowUserData userdata,
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);

Expand Down
27 changes: 23 additions & 4 deletions src/duckstation-qt/qtutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "core/game_list.h"
#include "core/system.h"

#include "util/gpu_device.h"

#include "common/error.h"
#include "common/log.h"

Expand All @@ -30,6 +32,8 @@
#include <QtWidgets/QTreeView>
#include <algorithm>
#include <array>
#include <cstdlib>
#include <cstring>
#include <map>

#if !defined(_WIN32) && !defined(APPLE)
Expand Down Expand Up @@ -317,7 +321,7 @@ qreal QtUtils::GetDevicePixelRatioForWidget(const QWidget* widget)
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
}

std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Error* error)
std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, RenderAPI render_api, Error* error)
{
WindowInfo wi;

Expand All @@ -333,8 +337,20 @@ std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Error
const QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("xcb"))
{
wi.type = WindowInfo::Type::X11;
wi.display_connection = pni->nativeResourceForWindow("display", widget->windowHandle());
// This is fucking ridiculous. NVIDIA+XWayland doesn't support Xlib, and NVIDIA+Xorg doesn't support XCB.
// Use Xlib if we're not running under Wayland, or we're not requesting OpenGL. Vulkan+XCB seems fine.
const char* xdg_session_type = std::getenv("XDG_SESSION_TYPE");
const bool is_running_on_xwayland = (xdg_session_type && std::strstr(xdg_session_type, "wayland"));
if (is_running_on_xwayland || render_api == RenderAPI::Vulkan)
{
wi.type = WindowInfo::Type::XCB;
wi.display_connection = pni->nativeResourceForWindow("connection", widget->windowHandle());
}
else
{
wi.type = WindowInfo::Type::Xlib;
wi.display_connection = pni->nativeResourceForWindow("display", widget->windowHandle());
}
wi.window_handle = reinterpret_cast<void*>(widget->winId());
}
else if (platform_name == QStringLiteral("wayland"))
Expand All @@ -356,9 +372,12 @@ std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Error
wi.surface_scale = static_cast<float>(dpr);

// Query refresh rate, we need it for sync.
std::optional<float> surface_refresh_rate = WindowInfo::QueryRefreshRateForWindow(wi);
Error refresh_rate_error;
std::optional<float> surface_refresh_rate = WindowInfo::QueryRefreshRateForWindow(wi, &refresh_rate_error);
if (!surface_refresh_rate.has_value())
{
WARNING_LOG("Failed to get refresh rate for window, falling back to Qt: {}", refresh_rate_error.GetDescription());

// Fallback to using the screen, getting the rate for Wayland is an utter mess otherwise.
const QScreen* widget_screen = widget->screen();
if (!widget_screen)
Expand Down
4 changes: 3 additions & 1 deletion src/duckstation-qt/qtutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class QVariant;
class QWidget;
class QUrl;

enum class RenderAPI : u8;

enum class ConsoleRegion : u8;
enum class DiscRegion : u8;
namespace GameDatabase {
Expand Down Expand Up @@ -116,7 +118,7 @@ QIcon GetIconForCompatibility(GameDatabase::CompatibilityRating rating);
qreal GetDevicePixelRatioForWidget(const QWidget* widget);

/// Returns the common window info structure for a Qt widget.
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget, Error* error = nullptr);
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget, RenderAPI render_api, Error* error = nullptr);

/// Saves a window's geometry to configuration. Returns false if the configuration was changed.
bool SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto_commit_changes = true);
Expand Down
12 changes: 9 additions & 3 deletions src/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ target_link_libraries(util PUBLIC common simpleini imgui)
target_link_libraries(util PRIVATE libchdr lzma JPEG::JPEG PNG::PNG WebP::libwebp lunasvg::lunasvg ZLIB::ZLIB SoundTouch::SoundTouchDLL xxhash Zstd::Zstd reshadefx)

if(ENABLE_X11)
target_sources(util PRIVATE
x11_tools.cpp
x11_tools.h
)
target_compile_definitions(util PRIVATE "-DENABLE_X11=1")
target_link_libraries(util PRIVATE X11::X11 X11::Xrandr)
target_link_libraries(util PRIVATE X11::xcb X11::xcb_randr X11::X11_xcb)
endif()

if(ENABLE_WAYLAND)
Expand Down Expand Up @@ -121,8 +125,10 @@ if(ENABLE_OPENGL)

if(ENABLE_X11)
target_sources(util PRIVATE
opengl_context_egl_x11.cpp
opengl_context_egl_x11.h
opengl_context_egl_xcb.cpp
opengl_context_egl_xcb.h
opengl_context_egl_xlib.cpp
opengl_context_egl_xlib.h
)
endif()
if(ENABLE_WAYLAND)
Expand Down
9 changes: 6 additions & 3 deletions src/util/opengl_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
#include "opengl_context_egl_wayland.h"
#endif
#ifdef ENABLE_X11
#include "opengl_context_egl_x11.h"
#include "opengl_context_egl_xcb.h"
#include "opengl_context_egl_xlib.h"
#endif
#endif
#endif
Expand Down Expand Up @@ -154,8 +155,10 @@ std::unique_ptr<OpenGLContext> OpenGLContext::Create(WindowInfo& wi, SurfaceHand
context = OpenGLContextEGLAndroid::Create(wi, surface, versions_to_try, error);
#else
#if defined(ENABLE_X11)
if (wi.type == WindowInfo::Type::X11)
context = OpenGLContextEGLX11::Create(wi, surface, versions_to_try, error);
if (wi.type == WindowInfo::Type::Xlib)
context = OpenGLContextEGLXlib::Create(wi, surface, versions_to_try, error);
else if (wi.type == WindowInfo::Type::XCB)
context = OpenGLContextEGLXCB::Create(wi, surface, versions_to_try, error);
#endif
#if defined(ENABLE_WAYLAND)
if (wi.type == WindowInfo::Type::Wayland)
Expand Down
Loading

0 comments on commit e69f0d3

Please sign in to comment.