From b8032c82863839d8e903575389e4dedd81f4fa37 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:21:27 +0300 Subject: [PATCH 1/4] Add Qt screen widget --- CMakeLists.txt | 12 ++++-- include/panda_qt/screen.hpp | 36 ++++++++++++++++ src/panda_qt/main.cpp | 19 +++++--- src/panda_qt/screen.cpp | 86 +++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 include/panda_qt/screen.hpp create mode 100644 src/panda_qt/screen.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b84dc470..254f2d2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,7 @@ if(ENABLE_DISCORD_RPC AND NOT ANDROID) include_directories(third_party/discord-rpc/include) endif() -if (ENABLE_QT_GUI) +if(ENABLE_QT_GUI) find_package(Qt6 REQUIRED COMPONENTS Widgets) # We can't use qt_standard_project_setup since it's Qt 6.3+ and we don't need to set the minimum that high @@ -179,9 +179,14 @@ set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp) # Frontend source files if(ENABLE_QT_GUI) - set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp) + set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp) + set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp) + + source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES}) + source_group("Header Files\\Qt" FILES ${FRONTEND_HEADER_FILES}) else() set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp) + set(FRONTEND_HEADER_FILES "") endif() set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp @@ -353,7 +358,8 @@ endif() source_group("Header Files\\Core" FILES ${HEADER_FILES}) set(ALL_SOURCES ${SOURCE_FILES} ${FRONTEND_SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES} - ${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${APPLET_SOURCE_FILES} ${RENDERER_SW_SOURCE_FILES} ${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) + ${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${APPLET_SOURCE_FILES} ${RENDERER_SW_SOURCE_FILES} ${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} + ${HEADER_FILES} ${FRONTEND_HEADER_FILES}) if(ENABLE_OPENGL) # Add the OpenGL source files to ALL_SOURCES diff --git a/include/panda_qt/screen.hpp b/include/panda_qt/screen.hpp new file mode 100644 index 00000000..bfee19a7 --- /dev/null +++ b/include/panda_qt/screen.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +#include "gl/context.h" +#include "window_info.h" + +// OpenGL widget for drawing the 3DS screen +class ScreenWidget : public QWidget { + Q_OBJECT + + public: + ScreenWidget(QWidget* parent = nullptr) : QWidget(parent) { + // Create a native window for use with our graphics API of choice + setAutoFillBackground(false); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOnScreen, true); + setAttribute(Qt::WA_KeyCompression, false); + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); + + if (!createGLContext()) { + Helpers::panic("Failed to create GL context for display"); + } + } + + private: + std::unique_ptr glContext = nullptr; + bool createGLContext(); + + qreal devicePixelRatioFromScreen() const; + int scaledWindowWidth() const; + int scaledWindowHeight() const; + std::optional getWindowInfo(); +}; diff --git a/src/panda_qt/main.cpp b/src/panda_qt/main.cpp index e8757f1a..723f7ef6 100644 --- a/src/panda_qt/main.cpp +++ b/src/panda_qt/main.cpp @@ -1,12 +1,17 @@ #include #include +#include "panda_qt/screen.hpp" + int main(int argc, char *argv[]) { - QApplication app(argc, argv); - QWidget window; - - window.resize(320, 240); - window.show(); - window.setWindowTitle("Alber"); - return app.exec(); + QApplication app(argc, argv); + QWidget window; + + window.resize(320, 240); + window.show(); + window.setWindowTitle("Alber"); + ScreenWidget screen(&window); + screen.show(); + + return app.exec(); } \ No newline at end of file diff --git a/src/panda_qt/screen.cpp b/src/panda_qt/screen.cpp new file mode 100644 index 00000000..84bfa48d --- /dev/null +++ b/src/panda_qt/screen.cpp @@ -0,0 +1,86 @@ +#include "panda_qt/screen.hpp" + +#include +#include +#include +#include +#include +#include +#include + +// OpenGL screen widget, based on https://github.com/melonDS-emu/melonDS/blob/master/src/frontend/qt_sdl/main.cpp + +#ifdef PANDA3DS_ENABLE_OPENGL +bool ScreenWidget::createGLContext() { + // List of GL context versions we will try. Anything 4.1+ is good + static constexpr std::array versionsToTry = { + GL::Context::Version{GL::Context::Profile::Core, 4, 6}, GL::Context::Version{GL::Context::Profile::Core, 4, 5}, + GL::Context::Version{GL::Context::Profile::Core, 4, 4}, GL::Context::Version{GL::Context::Profile::Core, 4, 3}, + GL::Context::Version{GL::Context::Profile::Core, 4, 2}, GL::Context::Version{GL::Context::Profile::Core, 4, 1}, + }; + + std::optional windowInfo = getWindowInfo(); + if (windowInfo.has_value()) { + glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); + glContext->DoneCurrent(); + } + + return glContext != nullptr; +} + +qreal ScreenWidget::devicePixelRatioFromScreen() const { + const QScreen* screenForRatio = window()->windowHandle()->screen(); + if (!screenForRatio) { + screenForRatio = QGuiApplication::primaryScreen(); + } + + return screenForRatio ? screenForRatio->devicePixelRatio() : static_cast(1); +} + +int ScreenWidget::scaledWindowWidth() const { + return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())), 1); +} + +int ScreenWidget::scaledWindowHeight() const { + return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())), 1); +} + +std::optional ScreenWidget::getWindowInfo() { + WindowInfo wi; + +// Windows and Apple are easy here since there's no display connection. +#if defined(_WIN32) + wi.type = WindowInfo::Type::Win32; + wi.window_handle = reinterpret_cast(winId()); +#elif defined(__APPLE__) + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = reinterpret_cast(winId()); +#else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + const QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("xcb")) { + wi.type = WindowInfo::Type::X11; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = reinterpret_cast(winId()); + } else if (platform_name == QStringLiteral("wayland")) { + wi.type = WindowInfo::Type::Wayland; + QWindow* handle = windowHandle(); + if (handle == nullptr) { + return std::nullopt; + } + + wi.display_connection = pni->nativeResourceForWindow("display", handle); + wi.window_handle = pni->nativeResourceForWindow("surface", handle); + } else { + qCritical() << "Unknown PNI platform " << platform_name; + return std::nullopt; + } +#endif + + wi.surface_width = static_cast(scaledWindowWidth()); + wi.surface_height = static_cast(scaledWindowHeight()); + wi.surface_scale = static_cast(devicePixelRatioFromScreen()); + + return wi; +} +#endif \ No newline at end of file From 45a80c9e39923b5dda98c7f6b89459730e443e8e Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:44:54 +0300 Subject: [PATCH 2/4] Better GL screen widget, initialize it to red as a test --- include/panda_qt/screen.hpp | 15 +------------- src/panda_qt/main.cpp | 1 + src/panda_qt/screen.cpp | 39 ++++++++++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/include/panda_qt/screen.hpp b/include/panda_qt/screen.hpp index bfee19a7..1555e463 100644 --- a/include/panda_qt/screen.hpp +++ b/include/panda_qt/screen.hpp @@ -10,20 +10,7 @@ class ScreenWidget : public QWidget { Q_OBJECT public: - ScreenWidget(QWidget* parent = nullptr) : QWidget(parent) { - // Create a native window for use with our graphics API of choice - setAutoFillBackground(false); - setAttribute(Qt::WA_NativeWindow, true); - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_PaintOnScreen, true); - setAttribute(Qt::WA_KeyCompression, false); - setFocusPolicy(Qt::StrongFocus); - setMouseTracking(true); - - if (!createGLContext()) { - Helpers::panic("Failed to create GL context for display"); - } - } + ScreenWidget(QWidget* parent = nullptr); private: std::unique_ptr glContext = nullptr; diff --git a/src/panda_qt/main.cpp b/src/panda_qt/main.cpp index 723f7ef6..34e71915 100644 --- a/src/panda_qt/main.cpp +++ b/src/panda_qt/main.cpp @@ -12,6 +12,7 @@ int main(int argc, char *argv[]) { window.setWindowTitle("Alber"); ScreenWidget screen(&window); screen.show(); + screen.resize(320, 240); return app.exec(); } \ No newline at end of file diff --git a/src/panda_qt/screen.cpp b/src/panda_qt/screen.cpp index 84bfa48d..4d21bbc7 100644 --- a/src/panda_qt/screen.cpp +++ b/src/panda_qt/screen.cpp @@ -1,16 +1,45 @@ -#include "panda_qt/screen.hpp" - +#include "opengl.hpp" +// opengl.hpp must be included at the very top. This comment exists to make clang-format not reorder it :p +#include +#include +#include #include #include #include #include -#include -#include -#include + +#include "panda_qt/screen.hpp" // OpenGL screen widget, based on https://github.com/melonDS-emu/melonDS/blob/master/src/frontend/qt_sdl/main.cpp #ifdef PANDA3DS_ENABLE_OPENGL +ScreenWidget::ScreenWidget(QWidget* parent) : QWidget(parent) { + // Create a native window for use with our graphics API of choice + setAutoFillBackground(false); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOnScreen, true); + setAttribute(Qt::WA_KeyCompression, false); + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); + + if (!createGLContext()) { + Helpers::panic("Failed to create GL context for display"); + } + + // Make our context current to use it + glContext->MakeCurrent(); + // Enable VSync for now + glContext->SetSwapInterval(1); + + OpenGL::setViewport(320, 240); + OpenGL::setClearColor(1.0, 0.0, 0.0, 1.0); + OpenGL::clearColor(); + + // Swap buffers to display our red as a test + glContext->SwapBuffers(); +} + bool ScreenWidget::createGLContext() { // List of GL context versions we will try. Anything 4.1+ is good static constexpr std::array versionsToTry = { From 8c3dd70c5a671ba46fbab174bb0a20a00aa6f30d Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:57:54 +0300 Subject: [PATCH 3/4] Add QPlatformNative include for Linux/BSD --- .github/workflows/Qt_Build.yml | 2 +- CMakeLists.txt | 1 + src/panda_qt/screen.cpp | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Qt_Build.yml b/.github/workflows/Qt_Build.yml index a9bed901..3cb25dd2 100644 --- a/.github/workflows/Qt_Build.yml +++ b/.github/workflows/Qt_Build.yml @@ -108,7 +108,7 @@ jobs: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 sudo add-apt-repository -y ppa:savoury1/qt-6-2 sudo apt update - sudo apt install qt6-base-dev + sudo apt install qt6-base-dev qt6-base-private-dev - name: Install newer Clang run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 254f2d2f..f5463bc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,7 @@ if(ENABLE_QT_GUI) source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES}) source_group("Header Files\\Qt" FILES ${FRONTEND_HEADER_FILES}) + include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS}) else() set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp) set(FRONTEND_HEADER_FILES "") diff --git a/src/panda_qt/screen.cpp b/src/panda_qt/screen.cpp index 4d21bbc7..62cac106 100644 --- a/src/panda_qt/screen.cpp +++ b/src/panda_qt/screen.cpp @@ -8,9 +8,14 @@ #include #include +#if !defined(_WIN32) && !defined(APPLE) +#include +#endif + #include "panda_qt/screen.hpp" -// OpenGL screen widget, based on https://github.com/melonDS-emu/melonDS/blob/master/src/frontend/qt_sdl/main.cpp +// OpenGL screen widget, based on https://github.com/stenzek/duckstation/blob/master/src/duckstation-qt/displaywidget.cpp +// and https://github.com/melonDS-emu/melonDS/blob/master/src/frontend/qt_sdl/main.cpp #ifdef PANDA3DS_ENABLE_OPENGL ScreenWidget::ScreenWidget(QWidget* parent) : QWidget(parent) { From 55bb0d71707520d084c5f3b5677b1aebe0628aa7 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 30 Sep 2023 15:01:28 +0300 Subject: [PATCH 4/4] Link EGL and GLX on Linux --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6f1e79f..22294821 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -407,6 +407,11 @@ if(ENABLE_QT_GUI) if(LINUX OR FREEBSD) find_package(X11 REQUIRED) target_link_libraries(Alber PRIVATE ${X11_LIBRARIES}) + + if(ENABLE_OPENGL) + find_package(OpenGL REQUIRED COMPONENTS OpenGL EGL GLX) + target_link_libraries(Alber PRIVATE OpenGL::OpenGL OpenGL::EGL OpenGL::GLX) + endif() endif() else() target_compile_definitions(Alber PUBLIC "PANDA3DS_FRONTEND_SDL=1")