Add Metal support to Qt frontend and clean up renderer creation code (#795)

* Qt: Initial support for Metal renderer

* Clean up graphics context code

* Nits

* More nits

* Qt: Move screen-related stuff to own folder

* Qt: Make screen widget polymorphic

* Qt: Re-add Metal

* Add factory for screen widget

* Qt: Support compilation without Metal

* Qt: Fix build without Metal

* Oops

* oops
This commit is contained in:
wheremyfoodat
2025-07-26 23:13:08 +03:00
committed by GitHub
parent 8b0b1939cf
commit 0446bcdaa1
23 changed files with 331 additions and 146 deletions

View File

@@ -0,0 +1,64 @@
#pragma once
#include <QWidget>
#include <functional>
#include "gl/context.h"
#include "screen_layout.hpp"
#include "window_info.h"
// 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
public:
using ResizeCallback = std::function<void(u32, u32)>;
enum class API { OpenGL, Metal, Vulkan };
ScreenWidget(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
virtual ~ScreenWidget() {}
void resizeEvent(QResizeEvent* event) override;
virtual GL::Context* getGLContext() { return nullptr; }
virtual void* getMTKLayer() { return nullptr; }
// Dimensions of our output surface
u32 surfaceWidth = 0;
u32 surfaceHeight = 0;
WindowInfo windowInfo;
// Cached "previous" dimensions, used when resizing our window
u32 previousWidth = 0;
u32 previousHeight = 0;
API api = API::OpenGL;
// 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;
float topScreenSize = 0.5f;
void reloadScreenLayout(ScreenLayout::Layout newLayout, float newTopScreenSize);
// Creates a screen widget depending on the graphics API we're using
static ScreenWidget* getWidget(API api, ResizeCallback resizeCallback, QWidget* parent = 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;
virtual bool createContext() = 0;
virtual void resizeDisplay() = 0;
std::optional<WindowInfo> getWindowInfo();
private:
qreal devicePixelRatioFromScreen() const;
int scaledWindowWidth() const;
int scaledWindowHeight() const;
void reloadScreenCoordinates();
};

View 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;
};

View File

@@ -0,0 +1,18 @@
#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);
~ScreenWidgetMTL() override;
virtual void* getMTKLayer() override;
virtual bool createContext() override;
virtual void resizeDisplay() override;
};