Skip to content

Commit 0446bcd

Browse files
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
1 parent 8b0b193 commit 0446bcd

23 files changed

Lines changed: 332 additions & 147 deletions

File tree

CMakeLists.txt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ if(ENABLE_METAL AND APPLE)
675675

676676
target_sources(AlberCore PRIVATE ${RENDERER_MTL_SOURCE_FILES})
677677
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_METAL=1")
678-
target_include_directories(AlberCore PRIVATE third_party/metal-cpp)
678+
target_include_directories(AlberCore PUBLIC third_party/metal-cpp)
679679
# TODO: check if all of them are needed
680680
target_link_libraries(AlberCore PUBLIC "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
681681
endif()
@@ -730,18 +730,25 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
730730
option(GENERATE_QT_TRANSLATION "Generate Qt translation file" OFF)
731731
set(QT_LANGUAGES docs/translations)
732732

733-
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
733+
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
734734
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
735735
src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp
736736
src/panda_qt/thread_debugger.cpp src/panda_qt/cpu_debugger.cpp src/panda_qt/dsp_debugger.cpp src/panda_qt/input_window.cpp
737+
src/panda_qt/screen/screen.cpp src/panda_qt/screen/screen_gl.cpp src/panda_qt/screen/screen_mtl.cpp
737738
)
738-
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
739+
740+
set(FRONTEND_HEADER_FILES include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
739741
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp
740742
include/panda_qt/patch_window.hpp include/panda_qt/elided_label.hpp include/panda_qt/shader_editor.hpp
741743
include/panda_qt/thread_debugger.hpp include/panda_qt/cpu_debugger.hpp include/panda_qt/dsp_debugger.hpp
742-
include/panda_qt/disabled_widget_overlay.hpp include/panda_qt/input_window.hpp
744+
include/panda_qt/disabled_widget_overlay.hpp include/panda_qt/input_window.hpp include/panda_qt/screen/screen.hpp
745+
include/panda_qt/screen/screen_gl.hpp include/panda_qt/screen/screen_mtl.hpp
743746
)
744747

748+
if (APPLE AND ENABLE_METAL)
749+
set(FRONTEND_SOURCE_FILES ${FRONTEND_SOURCE_FILES} src/panda_qt/screen/metal_context.mm)
750+
endif()
751+
745752
source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES})
746753
source_group("Header Files\\Qt" FILES ${FRONTEND_HEADER_FILES})
747754
include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS})

include/PICA/gpu.hpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,7 @@ class GPU {
109109
void screenshot(const std::string& name) { renderer->screenshot(name); }
110110
void deinitGraphicsContext() { renderer->deinitGraphicsContext(); }
111111

112-
#if defined(PANDA3DS_FRONTEND_SDL)
113-
void initGraphicsContext(SDL_Window* window) { renderer->initGraphicsContext(window); }
114-
#elif defined(PANDA3DS_FRONTEND_QT)
115-
void initGraphicsContext(GL::Context* context) { renderer->initGraphicsContext(context); }
116-
#endif
112+
void initGraphicsContext(void* context) { renderer->initGraphicsContext(context); }
117113

118114
void fireDMA(u32 dest, u32 source, u32 size);
119115
void reset();

include/emulator.hpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,8 @@ class Emulator {
106106
bool loadELF(const std::filesystem::path& path);
107107
bool loadELF(std::ifstream& file);
108108

109-
#ifdef PANDA3DS_FRONTEND_QT
110-
// For passing the GL context from Qt to the renderer
111-
void initGraphicsContext(GL::Context* glContext) { gpu.initGraphicsContext(nullptr); }
112-
#else
113-
void initGraphicsContext(SDL_Window* window) { gpu.initGraphicsContext(window); }
114-
#endif
109+
// For passing the SDL Window, GL context, etc from the frontend to the renderer
110+
void initGraphicsContext(void* context) { gpu.initGraphicsContext(context); }
115111

116112
RomFS::DumpingResult dumpRomFS(const std::filesystem::path& path);
117113
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }

include/panda_qt/main_window.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include "panda_qt/cpu_debugger.hpp"
2121
#include "panda_qt/dsp_debugger.hpp"
2222
#include "panda_qt/patch_window.hpp"
23-
#include "panda_qt/screen.hpp"
23+
#include "panda_qt/screen/screen.hpp"
2424
#include "panda_qt/shader_editor.hpp"
2525
#include "panda_qt/text_editor.hpp"
2626
#include "panda_qt/thread_debugger.hpp"
@@ -136,7 +136,7 @@ class MainWindow : public QMainWindow {
136136
void loadKeybindings();
137137
void saveKeybindings();
138138

139-
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
139+
// Tracks what graphics API is backing our renderer
140140
bool usingGL = false;
141141
bool usingVk = false;
142142
bool usingMtl = false;
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
#pragma once
22
#include <QWidget>
33
#include <functional>
4-
#include <memory>
54

65
#include "gl/context.h"
76
#include "screen_layout.hpp"
87
#include "window_info.h"
98

10-
// OpenGL widget for drawing the 3DS screen
9+
// Abstract screen widget for drawing the 3DS screen. We've got a child class for each graphics API (ScreenWidgetGL, ScreenWidgetMTL, ...)
1110
class ScreenWidget : public QWidget {
1211
Q_OBJECT
1312

1413
public:
1514
using ResizeCallback = std::function<void(u32, u32)>;
1615

17-
ScreenWidget(ResizeCallback resizeCallback, QWidget* parent = nullptr);
16+
enum class API { OpenGL, Metal, Vulkan };
17+
18+
ScreenWidget(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
19+
virtual ~ScreenWidget() {}
20+
1821
void resizeEvent(QResizeEvent* event) override;
19-
// Called by the emulator thread for resizing the actual GL surface, since the emulator thread owns the GL context
20-
void resizeSurface(u32 width, u32 height);
2122

22-
GL::Context* getGLContext() { return glContext.get(); }
23+
virtual GL::Context* getGLContext() { return nullptr; }
24+
virtual void* getMTKLayer() { return nullptr; }
2325

2426
// Dimensions of our output surface
2527
u32 surfaceWidth = 0;
@@ -30,25 +32,33 @@ class ScreenWidget : public QWidget {
3032
u32 previousWidth = 0;
3133
u32 previousHeight = 0;
3234

33-
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen regardless
34-
// of layout or resizing
35+
API api = API::OpenGL;
36+
37+
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen
3538
ScreenLayout::WindowCoordinates screenCoordinates;
3639
// Screen layouts and sizes
3740
ScreenLayout::Layout screenLayout = ScreenLayout::Layout::Default;
3841
float topScreenSize = 0.5f;
3942

4043
void reloadScreenLayout(ScreenLayout::Layout newLayout, float newTopScreenSize);
4144

42-
private:
43-
std::unique_ptr<GL::Context> glContext = nullptr;
45+
// Creates a screen widget depending on the graphics API we're using
46+
static ScreenWidget* getWidget(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
47+
48+
// Called by the emulator thread on OpenGL for resizing the actual GL surface, since the emulator thread owns the GL context
49+
virtual void resizeSurface(u32 width, u32 height) {};
50+
51+
protected:
4452
ResizeCallback resizeCallback;
4553

46-
bool createGLContext();
54+
virtual bool createContext() = 0;
55+
virtual void resizeDisplay() = 0;
56+
std::optional<WindowInfo> getWindowInfo();
4757

58+
private:
4859
qreal devicePixelRatioFromScreen() const;
4960
int scaledWindowWidth() const;
5061
int scaledWindowHeight() const;
51-
std::optional<WindowInfo> getWindowInfo();
5262

5363
void reloadScreenCoordinates();
5464
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
#include <memory>
3+
4+
#include "gl/context.h"
5+
#include "panda_qt/screen/screen.hpp"
6+
7+
class ScreenWidgetGL : public ScreenWidget {
8+
std::unique_ptr<GL::Context> glContext = nullptr;
9+
10+
public:
11+
ScreenWidgetGL(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
12+
13+
virtual GL::Context* getGLContext() override;
14+
virtual bool createContext() override;
15+
16+
virtual void resizeDisplay() override;
17+
virtual void resizeSurface(u32 width, u32 height) override;
18+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
#include "panda_qt/screen/screen.hpp"
3+
4+
class ScreenWidgetMTL : public ScreenWidget {
5+
void* mtkLayer = nullptr;
6+
7+
// Objective-C++ functions for handling the Metal context
8+
bool createMetalContext();
9+
void resizeMetalView();
10+
11+
public:
12+
ScreenWidgetMTL(API api, ResizeCallback resizeCallback, QWidget* parent = nullptr);
13+
~ScreenWidgetMTL() override;
14+
15+
virtual void* getMTKLayer() override;
16+
virtual bool createContext() override;
17+
virtual void resizeDisplay() override;
18+
};

include/renderer.hpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class Renderer {
6969

7070
virtual void reset() = 0;
7171
virtual void display() = 0; // Display the 3DS screen contents to the window
72-
virtual void initGraphicsContext(SDL_Window* window) = 0; // Initialize graphics context
72+
virtual void initGraphicsContext(void* context) = 0; // Initialize graphics context
7373
virtual void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) = 0; // Clear a GPU buffer in VRAM
7474
virtual void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) = 0; // Perform display transfer
7575
virtual void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) = 0;
@@ -91,21 +91,16 @@ class Renderer {
9191
// Called to notify the core to use OpenGL ES and not desktop GL
9292
virtual void setupGLES() {}
9393

94-
// Only relevant for Metal renderer on iOS
95-
// Passes a SwiftUI MTKView's layer (CAMetalLayer) to the renderer
96-
virtual void setMTKLayer(void* layer) {};
94+
// Used for Metal renderer on Qt and iOS
95+
// Passes an NSView's backing layer (CAMetalLayer) to the renderer
96+
virtual void setMTKLayer(void* layer) { Helpers::panic("Renderer doesn't support MTK Layer"); };
9797

9898
// This function is called on every draw call before parsing vertex data.
9999
// It is responsible for things like looking up which vertex/fragment shaders to use, recompiling them if they don't exist, choosing between
100100
// ubershaders and shadergen, and so on.
101101
// Returns whether this draw is eligible for using hardware-accelerated shaders or if shaders should run on the CPU
102102
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) { return false; }
103103

104-
// Functions for initializing the graphics context for the Qt frontend, where we don't have the convenience of SDL_Window
105-
#ifdef PANDA3DS_FRONTEND_QT
106-
virtual void initGraphicsContext(GL::Context* context) { Helpers::panic("Tried to initialize incompatible renderer with GL context"); }
107-
#endif
108-
109104
void setFBSize(u32 width, u32 height) {
110105
fbSize[0] = width;
111106
fbSize[1] = height;

include/renderer_gl/renderer_gl.hpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class RendererGL final : public Renderer {
191191

192192
void reset() override;
193193
void display() override; // Display the 3DS screen contents to the window
194-
void initGraphicsContext(SDL_Window* window) override; // Initialize graphics context
194+
void initGraphicsContext(void* context) override; // Initialize graphics context
195195
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM
196196
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer
197197
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
@@ -211,10 +211,6 @@ class RendererGL final : public Renderer {
211211
void resetStateManager() { gl.reset(); }
212212
void initUbershader(OpenGL::Program& program);
213213

214-
#ifdef PANDA3DS_FRONTEND_QT
215-
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override { initGraphicsContextInternal(); }
216-
#endif
217-
218214
// Take a screenshot of the screen and store it in a file
219215
void screenshot(const std::string& name) override;
220216
};

include/renderer_mtl/renderer_mtl.hpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include "mtl_vertex_buffer_cache.hpp"
1414
#include "renderer.hpp"
1515

16-
1716
// HACK: use the OpenGL cache
1817
#include "../renderer_gl/surface_cache.hpp"
1918

@@ -30,18 +29,14 @@ class RendererMTL final : public Renderer {
3029

3130
void reset() override;
3231
void display() override;
33-
void initGraphicsContext(SDL_Window* window) override;
32+
void initGraphicsContext(void* context) override;
3433
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override;
3534
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override;
3635
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
3736
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
3837
void screenshot(const std::string& name) override;
3938
void deinitGraphicsContext() override;
4039

41-
#ifdef PANDA3DS_FRONTEND_QT
42-
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
43-
#endif
44-
4540
virtual void setMTKLayer(void* layer) override;
4641

4742
private:

0 commit comments

Comments
 (0)