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

@@ -10,6 +10,7 @@
#include "cheats.hpp"
#include "input_mappings.hpp"
#include "panda_qt/dsp_debugger.hpp"
#include "panda_qt/screen/screen.hpp"
#include "sdl_sensors.hpp"
#include "services/dsp.hpp"
#include "version.hpp"
@@ -25,8 +26,19 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
resize(800, 240 * 4);
show();
const RendererType rendererType = emu->getConfig().rendererType;
usingGL = (rendererType == RendererType::OpenGL || rendererType == RendererType::Software || rendererType == RendererType::Null);
usingVk = (rendererType == RendererType::Vulkan);
usingMtl = (rendererType == RendererType::Metal);
ScreenWidget::API api = ScreenWidget::API::OpenGL;
if (usingVk)
api = ScreenWidget::API::Vulkan;
else if (usingMtl)
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([this](u32 width, u32 height) { handleScreenResize(width, height); }, this);
screen = ScreenWidget::getWidget(api, [this](u32 width, u32 height) { handleScreenResize(width, height); }, this);
setCentralWidget(screen);
appRunning = true;
@@ -149,28 +161,29 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
// The emulator graphics context for the thread should be initialized in the emulator thread due to how GL contexts work
emuThread = std::thread([this]() {
const RendererType rendererType = emu->getConfig().rendererType;
usingGL = (rendererType == RendererType::OpenGL || rendererType == RendererType::Software || rendererType == RendererType::Null);
usingVk = (rendererType == RendererType::Vulkan);
usingMtl = (rendererType == RendererType::Metal);
switch (screen->api) {
case ScreenWidget::API::OpenGL: {
// Make GL context current for this thread, enable VSync
GL::Context* glContext = screen->getGLContext();
glContext->MakeCurrent();
glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0);
if (usingGL) {
// Make GL context current for this thread, enable VSync
GL::Context* glContext = screen->getGLContext();
glContext->MakeCurrent();
glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0);
if (glContext->IsGLES()) {
emu->getRenderer()->setupGLES();
}
if (glContext->IsGLES()) {
emu->getRenderer()->setupGLES();
emu->initGraphicsContext(glContext);
break;
}
emu->initGraphicsContext(glContext);
} else if (usingVk) {
Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!");
} else if (usingMtl) {
Helpers::panic("Metal on Qt currently doesn't work, try the SDL frontend instead!");
} else {
Helpers::panic("Unsupported graphics backend for Qt frontend!");
case ScreenWidget::API::Metal: {
emu->initGraphicsContext(nullptr);
emu->getRenderer()->setMTKLayer(screen->getMTKLayer());
break;
}
case ScreenWidget::API::Vulkan: Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!"); break;
default: Helpers::panic("Unsupported graphics backend for Qt frontend!"); break;
}
// We have to initialize controllers on the same thread they'll be polled in
@@ -213,6 +226,8 @@ void MainWindow::emuThreadMainLoop() {
void MainWindow::swapEmuBuffer() {
if (usingGL) {
screen->getGLContext()->SwapBuffers();
} else if (usingMtl) {
// The renderer itself calls presentDrawable to swap buffers on Metal
} else {
Helpers::panic("[Qt] Don't know how to swap buffers for the current rendering backend :(");
}
@@ -290,6 +305,7 @@ MainWindow::~MainWindow() {
delete aboutWindow;
delete configWindow;
delete cheatsEditor;
delete screen;
delete luaEditor;
}