mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-08-08 09:08:43 +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/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/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
|
||||
|
@ -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/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/screen/screen_gl.hpp include/panda_qt/screen/screen_mtl.hpp
|
||||
)
|
||||
|
||||
if (APPLE AND ENABLE_METAL)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "screen_layout.hpp"
|
||||
#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 {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -18,11 +18,9 @@ class ScreenWidget : public QWidget {
|
|||
|
||||
ScreenWidget(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
|
||||
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(); }
|
||||
void* getMTKLayer() { return mtkLayer; }
|
||||
virtual GL::Context* getGLContext() { return nullptr; }
|
||||
virtual void* getMTKLayer() { return nullptr; }
|
||||
|
||||
// Dimensions of our output surface
|
||||
u32 surfaceWidth = 0;
|
||||
|
@ -35,8 +33,7 @@ class ScreenWidget : public QWidget {
|
|||
|
||||
API api = API::OpenGL;
|
||||
|
||||
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen regardless
|
||||
// of layout or resizing
|
||||
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen
|
||||
ScreenLayout::WindowCoordinates screenCoordinates;
|
||||
// Screen layouts and sizes
|
||||
ScreenLayout::Layout screenLayout = ScreenLayout::Layout::Default;
|
||||
|
@ -44,24 +41,20 @@ class ScreenWidget : public QWidget {
|
|||
|
||||
void reloadScreenLayout(ScreenLayout::Layout newLayout, float newTopScreenSize);
|
||||
|
||||
private:
|
||||
// GL context for GL-based renderers
|
||||
std::unique_ptr<GL::Context> glContext = nullptr;
|
||||
|
||||
// CA::MetalLayer for the Metal renderer
|
||||
void* mtkLayer = nullptr;
|
||||
// Called by the emulator thread on OpenGL for resizing the actual GL surface, since the emulator thread owns the GL context
|
||||
virtual void resizeSurface(u32 width, u32 height) {};
|
||||
|
||||
protected:
|
||||
ResizeCallback resizeCallback;
|
||||
|
||||
bool createGLContext();
|
||||
bool createMetalContext();
|
||||
|
||||
void resizeMetalView();
|
||||
virtual bool createContext() = 0;
|
||||
virtual void resizeDisplay() = 0;
|
||||
std::optional<WindowInfo> getWindowInfo();
|
||||
|
||||
private:
|
||||
qreal devicePixelRatioFromScreen() const;
|
||||
int scaledWindowWidth() const;
|
||||
int scaledWindowHeight() const;
|
||||
std::optional<WindowInfo> getWindowInfo();
|
||||
|
||||
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 "input_mappings.hpp"
|
||||
#include "panda_qt/dsp_debugger.hpp"
|
||||
#include "panda_qt/screen/screen_gl.hpp"
|
||||
#include "sdl_sensors.hpp"
|
||||
#include "services/dsp.hpp"
|
||||
#include "version.hpp"
|
||||
|
@ -37,7 +38,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||
api = ScreenWidget::API::Metal;
|
||||
|
||||
// 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);
|
||||
|
||||
appRunning = true;
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
#import <QWindow>
|
||||
#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();
|
||||
CAMetalLayer* metalLayer = [CAMetalLayer layer];
|
||||
|
||||
|
@ -41,7 +41,7 @@ bool ScreenWidget::createMetalContext() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ScreenWidget::resizeMetalView() {
|
||||
void ScreenWidgetMTL::resizeMetalView() {
|
||||
NSView* view = (NSView*)this->windowHandle()->winId();
|
||||
CAMetalLayer* metalLayer = (CAMetalLayer*)[view layer];
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||
#include "opengl.hpp"
|
||||
#endif
|
||||
// opengl.hpp must be included at the very top. This comment exists to make clang-format not reorder it :p
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
|
@ -12,9 +14,11 @@
|
|||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
#include "panda_qt/screen//screen_mtl.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
|
||||
|
||||
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||
|
@ -28,20 +32,7 @@ ScreenWidget::ScreenWidget(API api, ResizeCallback resizeCallback, QWidget* pare
|
|||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMouseTracking(true);
|
||||
|
||||
if (api == API::OpenGL) {
|
||||
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();
|
||||
// The graphics context, as well as resizing and showing the widget, is handled by the screen backend
|
||||
}
|
||||
|
||||
void ScreenWidget::resizeEvent(QResizeEvent* event) {
|
||||
|
@ -55,22 +46,8 @@ void ScreenWidget::resizeEvent(QResizeEvent* event) {
|
|||
this->windowInfo = *windowInfo;
|
||||
}
|
||||
|
||||
if (api == API::Metal) {
|
||||
resizeMetalView();
|
||||
}
|
||||
|
||||
reloadScreenCoordinates();
|
||||
// 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 ScreenWidget::resizeSurface(u32 width, u32 height) {
|
||||
if (api == API::OpenGL && (previousWidth != width || previousHeight != height)) {
|
||||
if (glContext) {
|
||||
glContext->ResizeSurface(width, height);
|
||||
}
|
||||
}
|
||||
resizeDisplay();
|
||||
}
|
||||
|
||||
void ScreenWidget::reloadScreenCoordinates() {
|
||||
|
@ -84,30 +61,6 @@ void ScreenWidget::reloadScreenLayout(ScreenLayout::Layout newLayout, float newT
|
|||
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 {
|
||||
const QScreen* screenForRatio = windowHandle()->screen();
|
||||
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