diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 4dd075e34..96d3ba6d4 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -20,8 +20,13 @@ add_library(ui STATIC qt_machinestatus.hpp qt_mediamenu.cpp qt_mediamenu.hpp - qt_gleswidget.cpp - qt_gleswidget.hpp + qt_rendererstack.cpp + qt_rendererstack.hpp + qt_rendererstack.ui + qt_softwarerenderer.cpp + qt_softwarerenderer.hpp + qt_hardwarerenderer.cpp + qt_hardwarerenderer.hpp qt_settings.cpp qt_settings.hpp diff --git a/src/qt/qt_gleswidget.hpp b/src/qt/qt_gleswidget.hpp deleted file mode 100644 index 467546c2c..000000000 --- a/src/qt/qt_gleswidget.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef WAYLAND -#include "wl_mouse.hpp" -#endif - -class GLESWidget : public QOpenGLWidget, protected QOpenGLFunctions -{ - Q_OBJECT - -private: - QImage m_image{QSize(2048 + 64, 2048 + 64), QImage::Format_RGB32}; - std::mutex image_mx; - int x, y, w, h, sx, sy, sw, sh; - bool wayland = false; -public: - void resizeGL(int w, int h) override; - void initializeGL() override; - void paintGL() override; - GLESWidget(QWidget* parent = nullptr) - : QOpenGLWidget(parent), QOpenGLFunctions() - { - setMinimumSize(16, 16); - setTextureFormat(GL_RGB); -#ifdef WAYLAND - if (QApplication::platformName().contains("wayland")) { - wayland = true; - wl_init(); - } -#endif - } - ~GLESWidget() - { - makeCurrent(); - } - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void wheelEvent(QWheelEvent *event) override; - void keyPressEvent(QKeyEvent* event) override - { - event->ignore(); - } - void keyReleaseEvent(QKeyEvent* event) override - { - event->ignore(); - } -signals: - void reqUpdate(); - -public slots: - void qt_real_blit(int x, int y, int w, int h); - void qt_mouse_poll(); - -private: - struct mouseinputdata { - int deltax, deltay, deltaz; - int mousebuttons; - }; - mouseinputdata mousedata; -}; diff --git a/src/qt/qt_hardwarerenderer.cpp b/src/qt/qt_hardwarerenderer.cpp new file mode 100644 index 000000000..db868ec7e --- /dev/null +++ b/src/qt/qt_hardwarerenderer.cpp @@ -0,0 +1,44 @@ +#include "qt_hardwarerenderer.hpp" + +void HardwareRenderer::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); +} + +void HardwareRenderer::initializeGL() +{ + initializeOpenGLFunctions(); +} + +void HardwareRenderer::paintGL() +{ + QPainter painter(this); + painter.drawImage(QRect(0, 0, width(), height()), image, QRect(sx, sy, sw, sh)); + // "release" image, reducing it's refcount, so renderstack::blit() + // won't have to reallocate + image = QImage(); +} + +void HardwareRenderer::setRenderType(RenderType type) { + QSurfaceFormat format; + switch (type) { + case RenderType::OpenGL: + setTextureFormat(GL_RGB); + format.setRenderableType(QSurfaceFormat::OpenGL); + break; + case RenderType::OpenGLES: + setTextureFormat(GL_RGBA); + format.setRenderableType(QSurfaceFormat::OpenGLES); + break; + } + setFormat(format); +} + +void HardwareRenderer::onBlit(const QImage& img, int x, int y, int w, int h) { + image = img; + sx = x; + sy = y; + sw = w; + sh = h; + update(); +} diff --git a/src/qt/qt_hardwarerenderer.hpp b/src/qt/qt_hardwarerenderer.hpp new file mode 100644 index 000000000..4d7f68a08 --- /dev/null +++ b/src/qt/qt_hardwarerenderer.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef WAYLAND +#include "wl_mouse.hpp" +#endif + +class HardwareRenderer : public QOpenGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT + +private: + bool wayland = false; +public: + void resizeGL(int w, int h) override; + void initializeGL() override; + void paintGL() override; + HardwareRenderer(QWidget* parent = nullptr) + : QOpenGLWidget(parent), QOpenGLFunctions() + { + setMinimumSize(16, 16); +#ifdef WAYLAND + if (QApplication::platformName().contains("wayland")) { + wayland = true; + wl_init(); + } +#endif + } + ~HardwareRenderer() + { + makeCurrent(); + } + + enum class RenderType { + OpenGL, + OpenGLES, + }; + void setRenderType(RenderType type); + +public slots: + void onBlit(const QImage& img, int, int, int, int); + +private: + QImage image; + int sx, sy, sw, sh; +}; diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 13f2bfed1..6bc350908 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -22,7 +22,6 @@ extern "C" { #include #include "qt_settings.hpp" -#include "qt_gleswidget.hpp" #include "qt_machinestatus.hpp" #include "qt_mediamenu.hpp" @@ -31,7 +30,6 @@ extern "C" { #include #endif -extern void qt_mouse_poll(); extern void qt_mouse_capture(int); extern "C" void qt_blit(int x, int y, int w, int h); @@ -45,28 +43,29 @@ MainWindow::MainWindow(QWidget *parent) : MediaMenu::ptr = mm; ui->setupUi(this); - video_setblit(qt_blit); - ui->glesWidget->setMouseTracking(true); + ui->stackedWidget->setMouseTracking(true); + ui->ogl->setRenderType(HardwareRenderer::RenderType::OpenGL); + ui->gles->setRenderType(HardwareRenderer::RenderType::OpenGLES); connect(this, &MainWindow::showMessageForNonQtThread, this, &MainWindow::showMessage_, Qt::BlockingQueuedConnection); connect(this, &MainWindow::setTitleForNonQtThread, this, &MainWindow::setTitle_, Qt::BlockingQueuedConnection); connect(this, &MainWindow::getTitleForNonQtThread, this, &MainWindow::getTitle_, Qt::BlockingQueuedConnection); - connect(this, &MainWindow::pollMouse, ui->glesWidget, &GLESWidget::qt_mouse_poll); + connect(this, &MainWindow::pollMouse, ui->stackedWidget, &RendererStack::mousePoll); connect(this, &MainWindow::setMouseCapture, this, [this](bool state) { mouse_capture = state ? 1 : 0; qt_mouse_capture(mouse_capture); if (mouse_capture) { - ui->glesWidget->grabMouse(); + ui->stackedWidget->grabMouse(); #ifdef WAYLAND if (QGuiApplication::platformName().contains("wayland")) { wl_mouse_capture(this->windowHandle()); } #endif } else { - ui->glesWidget->releaseMouse(); + ui->stackedWidget->releaseMouse(); #ifdef WAYLAND if (QGuiApplication::platformName().contains("wayland")) { wl_mouse_uncapture(); @@ -76,7 +75,7 @@ MainWindow::MainWindow(QWidget *parent) : }); connect(this, &MainWindow::resizeContents, this, [this](int w, int h) { - ui->glesWidget->resize(w, h); + ui->stackedWidget->resize(w, h); resize(w, h + menuBar()->height() + statusBar()->height()); }); @@ -94,12 +93,11 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionKeyboard_requires_capture->setChecked(kbd_req_capture); ui->actionRight_CTRL_is_left_ALT->setChecked(rctrl_is_lalt); + + video_setblit(qt_blit); } MainWindow::~MainWindow() { - //sdl_close(); - startblit(); - //delete hw_widget; delete ui; } @@ -721,7 +719,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) void MainWindow::blitToWidget(int x, int y, int w, int h) { - ui->glesWidget->qt_real_blit(x, y, w, h); + ui->stackedWidget->blit(x, y, w, h); } void MainWindow::keyReleaseEvent(QKeyEvent* event) @@ -732,3 +730,15 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) keyboard_input(0, x11_keycode_to_keysym(event->nativeScanCode())); #endif } + +void MainWindow::on_actionSoftware_Renderer_triggered() { + ui->stackedWidget->setCurrentIndex(0); +} + +void MainWindow::on_actionHardware_Renderer_OpenGL_triggered() { + ui->stackedWidget->setCurrentIndex(1); +} + +void MainWindow::on_actionHardware_Renderer_OpenGL_ES_triggered() { + ui->stackedWidget->setCurrentIndex(2); +} diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 816f7f568..d59931cbd 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -52,6 +52,9 @@ private slots: void on_actionHard_Reset_triggered(); void on_actionRight_CTRL_is_left_ALT_triggered(); void on_actionKeyboard_requires_capture_triggered(); + void on_actionHardware_Renderer_OpenGL_ES_triggered(); + void on_actionHardware_Renderer_OpenGL_triggered(); + void on_actionSoftware_Renderer_triggered(); void refreshMediaMenu(); void showMessage_(const QString& header, const QString& message); @@ -60,6 +63,7 @@ private slots: protected: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; + private: Ui::MainWindow *ui; std::unique_ptr status; diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 50fc3fa49..3541a5736 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -37,7 +37,14 @@ 0 - + + + 0 + + + + + @@ -77,6 +84,9 @@ View + + + @@ -146,12 +156,39 @@ Ctrl+Alt+PgUp + + + Software Renderer + + + + + Hardware Renderer (OpenGL) + + + + + Hardware Renderer (OpenGL ES) + + - GLESWidget + HardwareRenderer QOpenGLWidget -
qt_gleswidget.hpp
+
qt_hardwarerenderer.hpp
+
+ + SoftwareRenderer + QWidget +
qt_softwarerenderer.hpp
+ 1 +
+ + RendererStack + QStackedWidget +
qt_rendererstack.hpp
+ 1
diff --git a/src/qt/qt_gleswidget.cpp b/src/qt/qt_rendererstack.cpp similarity index 55% rename from src/qt/qt_gleswidget.cpp rename to src/qt/qt_rendererstack.cpp index 2348dc27e..1205632ad 100644 --- a/src/qt/qt_gleswidget.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -1,8 +1,9 @@ -#include -#include -#include -#include -#include "qt_gleswidget.hpp" +#include "qt_rendererstack.hpp" +#include "ui_qt_rendererstack.h" + +#include "qt_softwarerenderer.hpp" +#include "qt_hardwarerenderer.hpp" + #ifdef __APPLE__ #include #endif @@ -14,6 +15,21 @@ extern "C" #include <86box/video.h> } +RendererStack::RendererStack(QWidget *parent) : + QStackedWidget(parent), + ui(new Ui::RendererStack) +{ + ui->setupUi(this); + imagebufs = QVector(2); + imagebufs[0] = QImage{QSize(2048 + 64, 2048 + 64), QImage::Format_RGB32}; + imagebufs[1] = QImage{QSize(2048 + 64, 2048 + 64), QImage::Format_RGB32}; +} + +RendererStack::~RendererStack() +{ + delete ui; +} + extern "C" void macos_poll_mouse(); void qt_mouse_capture(int on) @@ -35,7 +51,7 @@ qt_mouse_capture(int on) return; } -void GLESWidget::qt_mouse_poll() +void RendererStack::mousePoll() { #ifdef __APPLE__ return macos_poll_mouse(); @@ -52,25 +68,7 @@ void GLESWidget::qt_mouse_poll() #endif } -void GLESWidget::resizeGL(int w, int h) -{ - glViewport(0, 0, w, h); -} - -void GLESWidget::initializeGL() -{ - initializeOpenGLFunctions(); - connect(this, &GLESWidget::reqUpdate, this, static_cast(&GLESWidget::update)); -} - -void GLESWidget::paintGL() -{ - std::scoped_lock lock(image_mx); - QPainter painter(this); - painter.drawImage(QRect(0, 0, width(), height()), m_image, QRect(sx, sy, sw, sh)); -} - -void GLESWidget::mouseReleaseEvent(QMouseEvent *event) +void RendererStack::mouseReleaseEvent(QMouseEvent *event) { if (this->geometry().contains(event->pos()) && event->button() == Qt::LeftButton && !mouse_capture) { @@ -89,7 +87,7 @@ void GLESWidget::mouseReleaseEvent(QMouseEvent *event) mousedata.mousebuttons &= ~event->button(); } } -void GLESWidget::mousePressEvent(QMouseEvent *event) +void RendererStack::mousePressEvent(QMouseEvent *event) { if (mouse_capture) { @@ -97,7 +95,7 @@ void GLESWidget::mousePressEvent(QMouseEvent *event) } event->accept(); } -void GLESWidget::wheelEvent(QWheelEvent *event) +void RendererStack::wheelEvent(QWheelEvent *event) { if (mouse_capture) { @@ -106,7 +104,7 @@ void GLESWidget::wheelEvent(QWheelEvent *event) } int ignoreNextMouseEvent = 0; -void GLESWidget::mouseMoveEvent(QMouseEvent *event) +void RendererStack::mouseMoveEvent(QMouseEvent *event) { if (QApplication::platformName().contains("wayland")) { @@ -128,27 +126,39 @@ void GLESWidget::mouseMoveEvent(QMouseEvent *event) #endif } -void GLESWidget::qt_real_blit(int x, int y, int w, int h) +// called from blitter thread +void RendererStack::blit(int x, int y, int w, int h) { + if ((w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL)) { - std::scoped_lock lock(image_mx); - if ((w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL)) - { - video_blit_complete(); - return; - } - sx = x; - sy = y; - sw = this->w = w; - sh = this->h = h; - auto imagebits = m_image.bits(); - video_copy(imagebits + y * ((2048 + 64) * 4) + x * 4, &(buffer32->line[y][x]), h * (2048 + 64) * sizeof(uint32_t)); - - if (screenshots) - { - video_screenshot((uint32_t *)imagebits, 0, 0, 2048 + 64); - } video_blit_complete(); + return; + } + sx = x; + sy = y; + sw = this->w = w; + sh = this->h = h; + auto imagebits = imagebufs[currentBuf].bits(); + video_copy(imagebits + y * ((2048 + 64) * 4) + x * 4, &(buffer32->line[y][x]), h * (2048 + 64) * sizeof(uint32_t)); + + if (screenshots) + { + video_screenshot((uint32_t *)imagebits, 0, 0, 2048 + 64); + } + video_blit_complete(); + blitToRenderer(imagebufs[currentBuf], sx, sy, sw, sh); + currentBuf = (currentBuf + 1) % 2; +} + +void RendererStack::on_RendererStack_currentChanged(int arg1) { + disconnect(this, &RendererStack::blitToRenderer, nullptr, nullptr); + switch (arg1) { + case 0: + connect(this, &RendererStack::blitToRenderer, dynamic_cast(currentWidget()), &SoftwareRenderer::onBlit); + break; + case 1: + case 2: + connect(this, &RendererStack::blitToRenderer, dynamic_cast(currentWidget()), &HardwareRenderer::onBlit); + break; } - reqUpdate(); } diff --git a/src/qt/qt_rendererstack.hpp b/src/qt/qt_rendererstack.hpp new file mode 100644 index 000000000..05dd5db5c --- /dev/null +++ b/src/qt/qt_rendererstack.hpp @@ -0,0 +1,60 @@ +#ifndef QT_RENDERERCONTAINER_HPP +#define QT_RENDERERCONTAINER_HPP + +#include +#include + +namespace Ui { +class RendererStack; +} + +class RendererStack : public QStackedWidget +{ + Q_OBJECT + +public: + explicit RendererStack(QWidget *parent = nullptr); + ~RendererStack(); + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void wheelEvent(QWheelEvent *event) override; + void keyPressEvent(QKeyEvent* event) override + { + event->ignore(); + } + void keyReleaseEvent(QKeyEvent* event) override + { + event->ignore(); + } + +signals: + void blitToRenderer(const QImage& img, int, int, int, int); + +public slots: + void blit(int x, int y, int w, int h); + void mousePoll(); + +private slots: + void on_RendererStack_currentChanged(int arg1); + +private: + Ui::RendererStack *ui; + + struct mouseinputdata { + int deltax, deltay, deltaz; + int mousebuttons; + }; + mouseinputdata mousedata; + + int x, y, w, h, sx, sy, sw, sh; + + // always have a qimage available for writing, which is _probably_ unused + // worst case - it will just get reallocated because it's refcounter is > 1 + // when calling bits(); + int currentBuf = 0; + QVector imagebufs; +}; + +#endif // QT_RENDERERCONTAINER_HPP diff --git a/src/qt/qt_rendererstack.ui b/src/qt/qt_rendererstack.ui new file mode 100644 index 000000000..634784714 --- /dev/null +++ b/src/qt/qt_rendererstack.ui @@ -0,0 +1,19 @@ + + + RendererStack + + + + 0 + 0 + 400 + 300 + + + + StackedWidget + + + + + diff --git a/src/qt/qt_softwarerenderer.cpp b/src/qt/qt_softwarerenderer.cpp new file mode 100644 index 000000000..9ecf6208f --- /dev/null +++ b/src/qt/qt_softwarerenderer.cpp @@ -0,0 +1,22 @@ +#include "qt_softwarerenderer.hpp" + +#include + +SoftwareRenderer::SoftwareRenderer(QWidget *parent) : QWidget(parent) {} + +void SoftwareRenderer::paintEvent(QPaintEvent *event) { + (void) event; + + QPainter painter(this); + painter.drawImage(QRect(0, 0, width(), height()), image, QRect(sx, sy, sw, sh)); + image = QImage(); +} + +void SoftwareRenderer::onBlit(const QImage& img, int x, int y, int w, int h) { + image = img; + sx = x; + sy = y; + sw = w; + sh = h; + update(); +} diff --git a/src/qt/qt_softwarerenderer.hpp b/src/qt/qt_softwarerenderer.hpp new file mode 100644 index 000000000..2a1075023 --- /dev/null +++ b/src/qt/qt_softwarerenderer.hpp @@ -0,0 +1,22 @@ +#ifndef SOFTWARERENDERER_HPP +#define SOFTWARERENDERER_HPP + +#include + +class SoftwareRenderer : public QWidget +{ + Q_OBJECT +public: + explicit SoftwareRenderer(QWidget *parent = nullptr); + + void paintEvent(QPaintEvent *event) override; + +public slots: + void onBlit(const QImage& img, int, int, int, int); + +private: + QImage image; + int sx, sy, sw, sh; +}; + +#endif // SOFTWARERENDERER_HPP