Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get emulator rendering working with Qt #303

Merged
merged 2 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion include/PICA/gpu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,15 @@ class GPU {
bool lightingLUTDirty = false;

GPU(Memory& mem, EmulatorConfig& config);
void initGraphicsContext(SDL_Window* window) { renderer->initGraphicsContext(window); }
void display() { renderer->display(); }
void screenshot(const std::string& name) { renderer->screenshot(name); }

#if defined(PANDA3DS_FRONTEND_SDL)
void initGraphicsContext(SDL_Window* window) { renderer->initGraphicsContext(window); }
#elif defined(PANDA3DS_FRONTEND_QT)
void initGraphicsContext(GL::Context* context) { renderer->initGraphicsContext(context); }
#endif

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

Expand Down
16 changes: 15 additions & 1 deletion include/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include "http_server.hpp"
#endif

#ifdef PANDA3DS_FRONTEND_QT
#include "gl/context.h"
#endif

enum class ROMType {
None,
ELF,
Expand All @@ -37,10 +41,12 @@ class Emulator {
Crypto::AESEngine aesEngine;
Cheats cheats;

#ifdef PANDA3DS_FRONTEND_SDL
SDL_Window* window;

#ifdef PANDA3DS_ENABLE_OPENGL
SDL_GLContext glContext;
#endif
#endif

SDL_GameController* gameController = nullptr;
Expand Down Expand Up @@ -106,5 +112,13 @@ class Emulator {
bool load3DSX(const std::filesystem::path& path);
bool loadELF(const std::filesystem::path& path);
bool loadELF(std::ifstream& file);
void initGraphicsContext();

#ifdef PANDA3DS_FRONTEND_QT
// For passing the GL context from Qt to the renderer
void initGraphicsContext(GL::Context* glContext) { gpu.initGraphicsContext(nullptr); }
#else
void initGraphicsContext() { gpu.initGraphicsContext(window); }
#endif

EmulatorConfig& getConfig() { return config; }
};
6 changes: 6 additions & 0 deletions include/panda_qt/main_window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#include <QMenuBar>
#include <QPalette>
#include <QtWidgets>
#include <thread>

#include "emulator.hpp"
#include "panda_qt/screen.hpp"

class MainWindow : public QMainWindow {
Expand All @@ -18,6 +20,10 @@ class MainWindow : public QMainWindow {
Dark = 2,
};

// This would normally be an std::unique_ptr but it's shared between threads so definitely not
Emulator* emu = nullptr;
std::thread emuThread;

QComboBox* themeSelect = nullptr;
QMenuBar* menuBar = nullptr;
ScreenWidget screen;
Expand Down
1 change: 1 addition & 0 deletions include/panda_qt/screen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ScreenWidget : public QWidget {

public:
ScreenWidget(QWidget* parent = nullptr);
GL::Context* getGLContext() { return glContext.get(); }

private:
std::unique_ptr<GL::Context> glContext = nullptr;
Expand Down
11 changes: 10 additions & 1 deletion include/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#include "PICA/regs.hpp"
#include "helpers.hpp"

#ifdef PANDA3DS_FRONTEND_QT
#include "gl/context.h"
#endif

enum class RendererType : s8 {
// Todo: Auto = -1,
Null = 0,
Expand Down Expand Up @@ -50,10 +54,15 @@ class Renderer {
virtual void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) = 0; // Clear a GPU buffer in VRAM
virtual void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) = 0; // Perform display transfer
virtual void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) = 0;
virtual void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) = 0; // Draw the given vertices
virtual void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) = 0; // Draw the given vertices

virtual void screenshot(const std::string& name) = 0;

// Functions for initializing the graphics context for the Qt frontend, where we don't have the convenience of SDL_Window
#ifdef PANDA3DS_FRONTEND_QT
virtual void initGraphicsContext(GL::Context* context) { Helpers::panic("Tried to initialize incompatible renderer with GL context"); }
#endif

void setFBSize(u32 width, u32 height) {
fbSize[0] = width;
fbSize[1] = height;
Expand Down
5 changes: 5 additions & 0 deletions include/renderer_gl/renderer_gl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class RendererGL final : public Renderer {
void setupTextureEnvState();
void bindTexturesToSlots();
void updateLightingLUT();
void initGraphicsContextInternal();

public:
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
Expand All @@ -83,6 +84,10 @@ class RendererGL final : public Renderer {

std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);

#ifdef PANDA3DS_FRONTEND_QT
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override { initGraphicsContextInternal(); }
#endif

// Take a screenshot of the screen and store it in a file
void screenshot(const std::string& name) override;
};
4 changes: 4 additions & 0 deletions include/renderer_null/renderer_null.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ class RendererNull final : public Renderer {
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
void screenshot(const std::string& name) override;

#ifdef PANDA3DS_FRONTEND_QT
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
#endif
};
4 changes: 4 additions & 0 deletions include/renderer_sw/renderer_sw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ class RendererSw final : public Renderer {
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
void screenshot(const std::string& name) override;

#ifdef PANDA3DS_FRONTEND_QT
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
#endif
};
6 changes: 5 additions & 1 deletion src/core/renderer_gl/renderer_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void RendererGL::reset() {
}
}

void RendererGL::initGraphicsContext(SDL_Window* window) {
void RendererGL::initGraphicsContextInternal() {
gl.reset();

auto gl_resources = cmrc::RendererGL::get_filesystem();
Expand Down Expand Up @@ -168,6 +168,10 @@ void RendererGL::initGraphicsContext(SDL_Window* window) {
reset();
}

// The OpenGL renderer doesn't need to do anything with the GL context (For Qt frontend) or the SDL window (For SDL frontend)
// So we just call initGraphicsContextInternal for both
void RendererGL::initGraphicsContext([[maybe_unused]] SDL_Window* window) { initGraphicsContextInternal(); }

// Set up the OpenGL blending context to match the emulated PICA
void RendererGL::setupBlending() {
// Map of PICA blending equations to OpenGL blending equations. The unused blending equations are equivalent to equation 0 (add)
Expand Down
9 changes: 6 additions & 3 deletions src/emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Emulator::Emulator()
}
#endif

// Only create SDL Window for SDL frontend
#ifdef PANDA3DS_FRONTEND_SDL
if (needOpenGL) {
// Demand 3.3 core for software renderer, or 4.1 core for OpenGL renderer (max available on MacOS)
// MacOS gets mad if we don't explicitly demand a core profile
Expand Down Expand Up @@ -71,6 +73,7 @@ Emulator::Emulator()
Helpers::warn("Window creation failed: %s", SDL_GetError());
}
}
#endif
#endif

if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) {
Expand Down Expand Up @@ -126,6 +129,8 @@ void Emulator::reset(ReloadOption reload) {
void Emulator::step() {}
void Emulator::render() {}

// Main loop for the SDL frontend. TODO: Move it to a dedicated file
#ifdef PANDA3DS_FRONTEND_SDL
void Emulator::run() {
programRunning = true;

Expand Down Expand Up @@ -403,6 +408,7 @@ void Emulator::run() {
SDL_GL_SwapWindow(window);
}
}
#endif

// Only resume if a ROM is properly loaded
void Emulator::resume() { running = (romType != ROMType::None); }
Expand Down Expand Up @@ -562,9 +568,6 @@ bool Emulator::loadELF(std::ifstream& file) {
return true;
}

// Reset our graphics context and initialize the GPU's graphics context
void Emulator::initGraphicsContext() { gpu.initGraphicsContext(window); }

#ifdef PANDA3DS_ENABLE_DISCORD_RPC
void Emulator::updateDiscord() {
if (config.discordRpcEnabled) {
Expand Down
37 changes: 35 additions & 2 deletions src/panda_qt/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
setWindowTitle("Alber");
// Enable drop events for loading ROMs
setAcceptDrops(true);
resize(320, 240);
resize(400, 240 * 2);
screen.show();

// Set our menu bar up
Expand All @@ -25,9 +25,42 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
themeSelect->setGeometry(40, 40, 100, 50);
themeSelect->show();
connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) { setTheme(static_cast<Theme>(index)); });

emu = new Emulator();

// The emulator graphics context for the thread should be initialized in the emulator thread due to how GL contexts work
emuThread = std::thread([&]() {
const RendererType rendererType = emu->getConfig().rendererType;

if (rendererType == RendererType::OpenGL || rendererType == RendererType::Software || rendererType == RendererType::Null) {
// Make GL context current for this thread, enable VSync
GL::Context* glContext = screen.getGLContext();
glContext->MakeCurrent();
glContext->SetSwapInterval(1);

emu->initGraphicsContext(glContext);
} else {
Helpers::panic("Unsupported renderer type for the Qt backend! Vulkan on Qt is currently WIP, try the SDL frontend instead!");
}

bool success = emu->loadROM("OoT.3ds");
if (!success) {
Helpers::panic("Failed to load ROM");
}

while (true) {
emu->runFrame();
screen.getGLContext()->SwapBuffers();
}
});
}

MainWindow::~MainWindow() { delete menuBar; }
// Cleanup when the main window closes
MainWindow::~MainWindow() {
delete emu;
delete menuBar;
delete themeSelect;
}

void MainWindow::setTheme(Theme theme) {
currentTheme = theme;
Expand Down
14 changes: 1 addition & 13 deletions src/panda_qt/screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#ifdef PANDA3DS_ENABLE_OPENGL
ScreenWidget::ScreenWidget(QWidget* parent) : QWidget(parent) {
// Create a native window for use with our graphics API of choice
resize(320, 240);
resize(400, 240 * 2);

setAutoFillBackground(false);
setAttribute(Qt::WA_NativeWindow, true);
Expand All @@ -33,18 +33,6 @@ ScreenWidget::ScreenWidget(QWidget* parent) : QWidget(parent) {
if (!createGLContext()) {
Helpers::panic("Failed to create GL context for display");
}

// Make our context current to use it
glContext->MakeCurrent();
// Enable VSync for now
glContext->SetSwapInterval(1);

OpenGL::setViewport(320, 240);
OpenGL::setClearColor(1.0, 0.0, 0.0, 1.0);
OpenGL::clearColor();

// Swap buffers to display our red as a test
glContext->SwapBuffers();
}

bool ScreenWidget::createGLContext() {
Expand Down
Loading