mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-08-08 17:18:42 +00:00
Qt: Make screen widget polymorphic
This commit is contained in:
parent
2cc94daae4
commit
1e34440479
9 changed files with 128 additions and 78 deletions
|
@ -734,7 +734,7 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
||||||
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp
|
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp
|
||||||
src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp
|
src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp
|
||||||
src/panda_qt/thread_debugger.cpp src/panda_qt/cpu_debugger.cpp src/panda_qt/dsp_debugger.cpp src/panda_qt/input_window.cpp
|
src/panda_qt/thread_debugger.cpp src/panda_qt/cpu_debugger.cpp src/panda_qt/dsp_debugger.cpp src/panda_qt/input_window.cpp
|
||||||
src/panda_qt/screen/screen.cpp
|
src/panda_qt/screen/screen.cpp src/panda_qt/screen/screen_gl.cpp src/panda_qt/screen/screen_mtl.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(FRONTEND_HEADER_FILES include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
|
set(FRONTEND_HEADER_FILES include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
|
||||||
|
@ -742,6 +742,7 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
||||||
include/panda_qt/patch_window.hpp include/panda_qt/elided_label.hpp include/panda_qt/shader_editor.hpp
|
include/panda_qt/patch_window.hpp include/panda_qt/elided_label.hpp include/panda_qt/shader_editor.hpp
|
||||||
include/panda_qt/thread_debugger.hpp include/panda_qt/cpu_debugger.hpp include/panda_qt/dsp_debugger.hpp
|
include/panda_qt/thread_debugger.hpp include/panda_qt/cpu_debugger.hpp include/panda_qt/dsp_debugger.hpp
|
||||||
include/panda_qt/disabled_widget_overlay.hpp include/panda_qt/input_window.hpp include/panda_qt/screen/screen.hpp
|
include/panda_qt/disabled_widget_overlay.hpp include/panda_qt/input_window.hpp include/panda_qt/screen/screen.hpp
|
||||||
|
include/panda_qt/screen/screen_gl.hpp include/panda_qt/screen/screen_mtl.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE AND ENABLE_METAL)
|
if (APPLE AND ENABLE_METAL)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "screen_layout.hpp"
|
#include "screen_layout.hpp"
|
||||||
#include "window_info.h"
|
#include "window_info.h"
|
||||||
|
|
||||||
// OpenGL widget for drawing the 3DS screen
|
// Abstract screen widget for drawing the 3DS screen. We've got a child class for each graphics API (ScreenWidgetGL, ScreenWidgetMTL, ...)
|
||||||
class ScreenWidget : public QWidget {
|
class ScreenWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -18,11 +18,9 @@ class ScreenWidget : public QWidget {
|
||||||
|
|
||||||
ScreenWidget(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
|
ScreenWidget(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
|
||||||
void resizeEvent(QResizeEvent* event) override;
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
// Called by the emulator thread for resizing the actual GL surface, since the emulator thread owns the GL context
|
|
||||||
void resizeSurface(u32 width, u32 height);
|
|
||||||
|
|
||||||
GL::Context* getGLContext() { return glContext.get(); }
|
virtual GL::Context* getGLContext() { return nullptr; }
|
||||||
void* getMTKLayer() { return mtkLayer; }
|
virtual void* getMTKLayer() { return nullptr; }
|
||||||
|
|
||||||
// Dimensions of our output surface
|
// Dimensions of our output surface
|
||||||
u32 surfaceWidth = 0;
|
u32 surfaceWidth = 0;
|
||||||
|
@ -35,8 +33,7 @@ class ScreenWidget : public QWidget {
|
||||||
|
|
||||||
API api = API::OpenGL;
|
API api = API::OpenGL;
|
||||||
|
|
||||||
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen regardless
|
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen
|
||||||
// of layout or resizing
|
|
||||||
ScreenLayout::WindowCoordinates screenCoordinates;
|
ScreenLayout::WindowCoordinates screenCoordinates;
|
||||||
// Screen layouts and sizes
|
// Screen layouts and sizes
|
||||||
ScreenLayout::Layout screenLayout = ScreenLayout::Layout::Default;
|
ScreenLayout::Layout screenLayout = ScreenLayout::Layout::Default;
|
||||||
|
@ -44,24 +41,20 @@ class ScreenWidget : public QWidget {
|
||||||
|
|
||||||
void reloadScreenLayout(ScreenLayout::Layout newLayout, float newTopScreenSize);
|
void reloadScreenLayout(ScreenLayout::Layout newLayout, float newTopScreenSize);
|
||||||
|
|
||||||
private:
|
// Called by the emulator thread on OpenGL for resizing the actual GL surface, since the emulator thread owns the GL context
|
||||||
// GL context for GL-based renderers
|
virtual void resizeSurface(u32 width, u32 height) {};
|
||||||
std::unique_ptr<GL::Context> glContext = nullptr;
|
|
||||||
|
|
||||||
// CA::MetalLayer for the Metal renderer
|
|
||||||
void* mtkLayer = nullptr;
|
|
||||||
|
|
||||||
|
protected:
|
||||||
ResizeCallback resizeCallback;
|
ResizeCallback resizeCallback;
|
||||||
|
|
||||||
bool createGLContext();
|
virtual bool createContext() = 0;
|
||||||
bool createMetalContext();
|
virtual void resizeDisplay() = 0;
|
||||||
|
std::optional<WindowInfo> getWindowInfo();
|
||||||
void resizeMetalView();
|
|
||||||
|
|
||||||
|
private:
|
||||||
qreal devicePixelRatioFromScreen() const;
|
qreal devicePixelRatioFromScreen() const;
|
||||||
int scaledWindowWidth() const;
|
int scaledWindowWidth() const;
|
||||||
int scaledWindowHeight() const;
|
int scaledWindowHeight() const;
|
||||||
std::optional<WindowInfo> getWindowInfo();
|
|
||||||
|
|
||||||
void reloadScreenCoordinates();
|
void reloadScreenCoordinates();
|
||||||
};
|
};
|
||||||
|
|
18
include/panda_qt/screen/screen_gl.hpp
Normal file
18
include/panda_qt/screen/screen_gl.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "gl/context.h"
|
||||||
|
#include "panda_qt/screen/screen.hpp"
|
||||||
|
|
||||||
|
class ScreenWidgetGL : public ScreenWidget {
|
||||||
|
std::unique_ptr<GL::Context> glContext = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScreenWidgetGL(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
virtual GL::Context* getGLContext() override;
|
||||||
|
virtual bool createContext() override;
|
||||||
|
|
||||||
|
virtual void resizeDisplay() override;
|
||||||
|
virtual void resizeSurface(u32 width, u32 height) override;
|
||||||
|
};
|
17
include/panda_qt/screen/screen_mtl.hpp
Normal file
17
include/panda_qt/screen/screen_mtl.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
#include "panda_qt/screen/screen.hpp"
|
||||||
|
|
||||||
|
class ScreenWidgetMTL : public ScreenWidget {
|
||||||
|
void* mtkLayer = nullptr;
|
||||||
|
|
||||||
|
// Objective-C++ functions for handling the Metal context
|
||||||
|
bool createMetalContext();
|
||||||
|
void resizeMetalView();
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScreenWidgetMTL(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
virtual void* getMTKLayer() override;
|
||||||
|
virtual bool createContext() override;
|
||||||
|
virtual void resizeDisplay() override;
|
||||||
|
};
|
|
@ -10,6 +10,7 @@
|
||||||
#include "cheats.hpp"
|
#include "cheats.hpp"
|
||||||
#include "input_mappings.hpp"
|
#include "input_mappings.hpp"
|
||||||
#include "panda_qt/dsp_debugger.hpp"
|
#include "panda_qt/dsp_debugger.hpp"
|
||||||
|
#include "panda_qt/screen/screen_gl.hpp"
|
||||||
#include "sdl_sensors.hpp"
|
#include "sdl_sensors.hpp"
|
||||||
#include "services/dsp.hpp"
|
#include "services/dsp.hpp"
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
|
@ -37,7 +38,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
||||||
api = ScreenWidget::API::Metal;
|
api = ScreenWidget::API::Metal;
|
||||||
|
|
||||||
// We pass a callback to the screen widget that will be triggered every time we resize the screen
|
// We pass a callback to the screen widget that will be triggered every time we resize the screen
|
||||||
screen = new ScreenWidget(api, [this](u32 width, u32 height) { handleScreenResize(width, height); }, this);
|
screen = new ScreenWidgetGL(api, [this](u32 width, u32 height) { handleScreenResize(width, height); }, this);
|
||||||
setCentralWidget(screen);
|
setCentralWidget(screen);
|
||||||
|
|
||||||
appRunning = true;
|
appRunning = true;
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
#import <QWindow>
|
#import <QWindow>
|
||||||
#import <QuartzCore/QuartzCore.hpp>
|
#import <QuartzCore/QuartzCore.hpp>
|
||||||
|
|
||||||
#import "panda_qt/screen/screen.hpp"
|
#import "panda_qt/screen/screen_mtl.hpp"
|
||||||
|
|
||||||
bool ScreenWidget::createMetalContext() {
|
bool ScreenWidgetMTL::createMetalContext() {
|
||||||
NSView* nativeView = (NSView*)this->winId();
|
NSView* nativeView = (NSView*)this->winId();
|
||||||
CAMetalLayer* metalLayer = [CAMetalLayer layer];
|
CAMetalLayer* metalLayer = [CAMetalLayer layer];
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ bool ScreenWidget::createMetalContext() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenWidget::resizeMetalView() {
|
void ScreenWidgetMTL::resizeMetalView() {
|
||||||
NSView* view = (NSView*)this->windowHandle()->winId();
|
NSView* view = (NSView*)this->windowHandle()->winId();
|
||||||
CAMetalLayer* metalLayer = (CAMetalLayer*)[view layer];
|
CAMetalLayer* metalLayer = (CAMetalLayer*)[view layer];
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
|
#endif
|
||||||
// opengl.hpp must be included at the very top. This comment exists to make clang-format not reorder it :p
|
// opengl.hpp must be included at the very top. This comment exists to make clang-format not reorder it :p
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -12,9 +14,11 @@
|
||||||
#include <qpa/qplatformnativeinterface.h>
|
#include <qpa/qplatformnativeinterface.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "panda_qt/screen//screen_mtl.hpp"
|
||||||
#include "panda_qt/screen/screen.hpp"
|
#include "panda_qt/screen/screen.hpp"
|
||||||
|
#include "panda_qt/screen/screen_gl.hpp"
|
||||||
|
|
||||||
// OpenGL screen widget, based on https://github.com/stenzek/duckstation/blob/master/src/duckstation-qt/displaywidget.cpp
|
// 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
|
// and https://github.com/melonDS-emu/melonDS/blob/master/src/frontend/qt_sdl/main.cpp
|
||||||
|
|
||||||
#ifdef PANDA3DS_ENABLE_OPENGL
|
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||||
|
@ -28,20 +32,7 @@ ScreenWidget::ScreenWidget(API api, ResizeCallback resizeCallback, QWidget* pare
|
||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
|
||||||
if (api == API::OpenGL) {
|
// The graphics context, as well as resizing and showing the widget, is handled by the screen backend
|
||||||
if (!createGLContext()) {
|
|
||||||
Helpers::panic("Failed to create GL context for display");
|
|
||||||
}
|
|
||||||
} else if (api == API::Metal) {
|
|
||||||
if (!createMetalContext()) {
|
|
||||||
Helpers::panic("Failed to create Metal context for display");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Helpers::panic("Unspported api for Qt screen widget");
|
|
||||||
}
|
|
||||||
|
|
||||||
resize(800, 240 * 4);
|
|
||||||
show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenWidget::resizeEvent(QResizeEvent* event) {
|
void ScreenWidget::resizeEvent(QResizeEvent* event) {
|
||||||
|
@ -55,22 +46,8 @@ void ScreenWidget::resizeEvent(QResizeEvent* event) {
|
||||||
this->windowInfo = *windowInfo;
|
this->windowInfo = *windowInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api == API::Metal) {
|
|
||||||
resizeMetalView();
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadScreenCoordinates();
|
reloadScreenCoordinates();
|
||||||
// This will call take care of calling resizeSurface from the emulator thread, as the GL renderer must resize from the emu thread
|
resizeDisplay();
|
||||||
resizeCallback(surfaceWidth, surfaceHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: This will run on the emulator thread, we don't want any Qt calls happening there.
|
|
||||||
void ScreenWidget::resizeSurface(u32 width, u32 height) {
|
|
||||||
if (api == API::OpenGL && (previousWidth != width || previousHeight != height)) {
|
|
||||||
if (glContext) {
|
|
||||||
glContext->ResizeSurface(width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenWidget::reloadScreenCoordinates() {
|
void ScreenWidget::reloadScreenCoordinates() {
|
||||||
|
@ -84,30 +61,6 @@ void ScreenWidget::reloadScreenLayout(ScreenLayout::Layout newLayout, float newT
|
||||||
reloadScreenCoordinates();
|
reloadScreenCoordinates();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenWidget::createGLContext() {
|
|
||||||
// List of GL context versions we will try. Anything 4.1+ is good for desktop OpenGL, and 3.1+ for OpenGL ES
|
|
||||||
static constexpr std::array<GL::Context::Version, 8> 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},
|
|
||||||
GL::Context::Version{GL::Context::Profile::ES, 3, 2}, GL::Context::Version{GL::Context::Profile::ES, 3, 1},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::optional<WindowInfo> windowInfo = getWindowInfo();
|
|
||||||
if (windowInfo.has_value()) {
|
|
||||||
this->windowInfo = *windowInfo;
|
|
||||||
|
|
||||||
glContext = GL::Context::Create(*getWindowInfo(), versionsToTry);
|
|
||||||
if (glContext == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glContext->DoneCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return glContext != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal ScreenWidget::devicePixelRatioFromScreen() const {
|
qreal ScreenWidget::devicePixelRatioFromScreen() const {
|
||||||
const QScreen* screenForRatio = windowHandle()->screen();
|
const QScreen* screenForRatio = windowHandle()->screen();
|
||||||
if (!screenForRatio) {
|
if (!screenForRatio) {
|
||||||
|
|
53
src/panda_qt/screen/screen_gl.cpp
Normal file
53
src/panda_qt/screen/screen_gl.cpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#include "panda_qt/screen/screen_gl.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
ScreenWidgetGL::ScreenWidgetGL(API api, ResizeCallback resizeCallback, QWidget* parent) : ScreenWidget(api, resizeCallback, parent) {
|
||||||
|
// On Wayland + OpenGL, we have to show the window before we can create a graphics context.
|
||||||
|
resize(800, 240 * 4);
|
||||||
|
show();
|
||||||
|
|
||||||
|
if (!createContext()) {
|
||||||
|
Helpers::panic("Failed to create GL context for display");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenWidgetGL::createContext() {
|
||||||
|
// List of GL context versions we will try. Anything 4.1+ is good for desktop OpenGL, and 3.1+ for OpenGL ES
|
||||||
|
static constexpr std::array<GL::Context::Version, 8> 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},
|
||||||
|
GL::Context::Version{GL::Context::Profile::ES, 3, 2}, GL::Context::Version{GL::Context::Profile::ES, 3, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<WindowInfo> windowInfo = getWindowInfo();
|
||||||
|
if (windowInfo.has_value()) {
|
||||||
|
this->windowInfo = *windowInfo;
|
||||||
|
|
||||||
|
glContext = GL::Context::Create(*getWindowInfo(), versionsToTry);
|
||||||
|
if (glContext == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glContext->DoneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return glContext != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenWidgetGL::resizeDisplay() {
|
||||||
|
// This will call take care of calling resizeSurface from the emulator thread, as the GL renderer must resize from the emu thread
|
||||||
|
resizeCallback(surfaceWidth, surfaceHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: This will run on the emulator thread, we don't want any Qt calls happening there.
|
||||||
|
void ScreenWidgetGL::resizeSurface(u32 width, u32 height) {
|
||||||
|
if (previousWidth != width || previousHeight != height) {
|
||||||
|
if (glContext) {
|
||||||
|
glContext->ResizeSurface(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL::Context* ScreenWidgetGL::getGLContext() { return glContext.get(); }
|
14
src/panda_qt/screen/screen_mtl.cpp
Normal file
14
src/panda_qt/screen/screen_mtl.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "panda_qt/screen/screen_mtl.hpp"
|
||||||
|
|
||||||
|
ScreenWidgetMTL::ScreenWidgetMTL(API api, ResizeCallback resizeCallback, QWidget* parent) : ScreenWidget(api, resizeCallback, parent) {
|
||||||
|
if (!createContext()) {
|
||||||
|
Helpers::panic("Failed to create Metal context for display");
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(800, 240 * 4);
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenWidgetMTL::resizeDisplay() { resizeMetalView(); }
|
||||||
|
bool ScreenWidgetMTL::createContext() { return createMetalContext(); }
|
||||||
|
void* ScreenWidgetMTL::getMTKLayer() { return mtkLayer; }
|
Loading…
Add table
Add a link
Reference in a new issue