Merge pull request #304 from wheremyfoodat/rhappy

Make emulator output size properly configurable
This commit is contained in:
wheremyfoodat 2023-10-01 17:27:33 +03:00 committed by GitHub
commit c5c0c423c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 53 additions and 13 deletions

View file

@ -113,6 +113,9 @@ class GPU {
u32 readInternalReg(u32 index); u32 readInternalReg(u32 index);
void writeInternalReg(u32 index, u32 value, u32 mask); void writeInternalReg(u32 index, u32 value, u32 mask);
// Used for setting the size of the window we'll be outputting graphics to
void setOutputSize(u32 width, u32 height) { renderer->setOutputSize(width, height); }
// TODO: Emulate the transfer engine & its registers // TODO: Emulate the transfer engine & its registers
// Then this can be emulated by just writing the appropriate values there // Then this can be emulated by just writing the appropriate values there
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { renderer->clearBuffer(startAddress, endAddress, value, control); } void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { renderer->clearBuffer(startAddress, endAddress, value, control); }

View file

@ -120,5 +120,7 @@ class Emulator {
void initGraphicsContext() { gpu.initGraphicsContext(window); } void initGraphicsContext() { gpu.initGraphicsContext(window); }
#endif #endif
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }
EmulatorConfig& getConfig() { return config; } EmulatorConfig& getConfig() { return config; }
}; };

View file

@ -30,6 +30,11 @@ class MainWindow : public QMainWindow {
Theme currentTheme; Theme currentTheme;
void setTheme(Theme theme); void setTheme(Theme theme);
void swapEmuBuffer();
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
bool usingGL = false;
bool usingVk = false;
public: public:
MainWindow(QApplication* app, QWidget* parent = nullptr); MainWindow(QApplication* app, QWidget* parent = nullptr);

View file

@ -13,6 +13,10 @@ class ScreenWidget : public QWidget {
ScreenWidget(QWidget* parent = nullptr); ScreenWidget(QWidget* parent = nullptr);
GL::Context* getGLContext() { return glContext.get(); } GL::Context* getGLContext() { return glContext.get(); }
// Dimensions of our output surface
u32 surfaceWidth = 0;
u32 surfaceHeight = 0;
private: private:
std::unique_ptr<GL::Context> glContext = nullptr; std::unique_ptr<GL::Context> glContext = nullptr;
bool createGLContext(); bool createGLContext();

View file

@ -40,6 +40,11 @@ class Renderer {
u32 depthBufferLoc; u32 depthBufferLoc;
PICA::DepthFmt depthBufferFormat; PICA::DepthFmt depthBufferFormat;
// Width and height of the window we're outputting to, needed for properly scaling the final image
// We initialize it to the 3DS resolution by default and the frontend can notify us if it changes via the setOutputSize function
u32 outputWindowWidth = 400;
u32 outputWindowHeight = 240 * 2;
public: public:
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs); Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
virtual ~Renderer(); virtual ~Renderer();
@ -78,4 +83,9 @@ class Renderer {
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; } void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; } void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; }
void setOutputSize(u32 width, u32 height) {
outputWindowWidth = width;
outputWindowHeight = height;
}
}; };

View file

@ -524,7 +524,7 @@ void RendererGL::display() {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
screenFramebuffer.bind(OpenGL::ReadFramebuffer); screenFramebuffer.bind(OpenGL::ReadFramebuffer);
glBlitFramebuffer(0, 0, 400, 480, 0, 0, 400, 480, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBlitFramebuffer(0, 0, 400, 480, 0, 0, outputWindowWidth, outputWindowHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
} }
void RendererGL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { void RendererGL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {

View file

@ -28,12 +28,6 @@ Emulator::Emulator()
Helpers::warn("Failed to initialize SDL2 GameController: %s", SDL_GetError()); Helpers::warn("Failed to initialize SDL2 GameController: %s", SDL_GetError());
} }
// We need OpenGL for software rendering or for OpenGL if it's enabled
bool needOpenGL = config.rendererType == RendererType::Software;
#ifdef PANDA3DS_ENABLE_OPENGL
needOpenGL = needOpenGL || (config.rendererType == RendererType::OpenGL);
#endif
#ifdef PANDA3DS_ENABLE_DISCORD_RPC #ifdef PANDA3DS_ENABLE_DISCORD_RPC
if (config.discordRpcEnabled) { if (config.discordRpcEnabled) {
discordRpc.init(); discordRpc.init();
@ -41,6 +35,12 @@ Emulator::Emulator()
} }
#endif #endif
// We need OpenGL for software rendering or for OpenGL if it's enabled
bool needOpenGL = config.rendererType == RendererType::Software;
#ifdef PANDA3DS_ENABLE_OPENGL
needOpenGL = needOpenGL || (config.rendererType == RendererType::OpenGL);
#endif
// Only create SDL Window for SDL frontend // Only create SDL Window for SDL frontend
#ifdef PANDA3DS_FRONTEND_SDL #ifdef PANDA3DS_FRONTEND_SDL
if (needOpenGL) { if (needOpenGL) {

View file

@ -17,9 +17,9 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
// Set up theme selection // Set up theme selection
setTheme(Theme::Dark); setTheme(Theme::Dark);
themeSelect = new QComboBox(this); themeSelect = new QComboBox(this);
themeSelect->addItem("System"); themeSelect->addItem(tr("System"));
themeSelect->addItem("Light"); themeSelect->addItem(tr("Light"));
themeSelect->addItem("Dark"); themeSelect->addItem(tr("Dark"));
themeSelect->setCurrentIndex(static_cast<int>(currentTheme)); themeSelect->setCurrentIndex(static_cast<int>(currentTheme));
themeSelect->setGeometry(40, 40, 100, 50); themeSelect->setGeometry(40, 40, 100, 50);
@ -27,20 +27,25 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) { setTheme(static_cast<Theme>(index)); }); connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) { setTheme(static_cast<Theme>(index)); });
emu = new Emulator(); emu = new Emulator();
emu->setOutputSize(screen.surfaceWidth, screen.surfaceHeight);
// The emulator graphics context for the thread should be initialized in the emulator thread due to how GL contexts work // The emulator graphics context for the thread should be initialized in the emulator thread due to how GL contexts work
emuThread = std::thread([&]() { emuThread = std::thread([&]() {
const RendererType rendererType = emu->getConfig().rendererType; const RendererType rendererType = emu->getConfig().rendererType;
usingGL = (rendererType == RendererType::OpenGL || rendererType == RendererType::Software || rendererType == RendererType::Null);
usingVk = (rendererType == RendererType::Vulkan);
if (rendererType == RendererType::OpenGL || rendererType == RendererType::Software || rendererType == RendererType::Null) { if (usingGL) {
// Make GL context current for this thread, enable VSync // Make GL context current for this thread, enable VSync
GL::Context* glContext = screen.getGLContext(); GL::Context* glContext = screen.getGLContext();
glContext->MakeCurrent(); glContext->MakeCurrent();
glContext->SetSwapInterval(1); glContext->SetSwapInterval(1);
emu->initGraphicsContext(glContext); emu->initGraphicsContext(glContext);
} else if (usingVk) {
Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!");
} else { } else {
Helpers::panic("Unsupported renderer type for the Qt backend! Vulkan on Qt is currently WIP, try the SDL frontend instead!"); Helpers::panic("Unsupported graphics backend for Qt frontend!");
} }
bool success = emu->loadROM("OoT.3ds"); bool success = emu->loadROM("OoT.3ds");
@ -50,11 +55,19 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
while (true) { while (true) {
emu->runFrame(); emu->runFrame();
screen.getGLContext()->SwapBuffers(); swapEmuBuffer();
} }
}); });
} }
void MainWindow::swapEmuBuffer() {
if (usingGL) {
screen.getGLContext()->SwapBuffers();
} else {
Helpers::panic("[Qt] Don't know how to swap buffers for the current rendering backend :(");
}
}
// Cleanup when the main window closes // Cleanup when the main window closes
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
delete emu; delete emu;

View file

@ -105,6 +105,9 @@ std::optional<WindowInfo> ScreenWidget::getWindowInfo() {
wi.surface_height = static_cast<u32>(scaledWindowHeight()); wi.surface_height = static_cast<u32>(scaledWindowHeight());
wi.surface_scale = static_cast<float>(devicePixelRatioFromScreen()); wi.surface_scale = static_cast<float>(devicePixelRatioFromScreen());
surfaceWidth = wi.surface_width;
surfaceHeight = wi.surface_height;
return wi; return wi;
} }
#endif #endif