From a74afc3f1ee17c2e61df0b1705823a5ca6620609 Mon Sep 17 00:00:00 2001 From: "Joakim L. Gilje" Date: Sat, 4 Dec 2021 21:33:04 +0100 Subject: [PATCH] added software renderer. split common functions prev. in gleswidget to renderstack (a QStackWidget), which in turn calls an actual renderer. added ability to target GLES directly, but this is maybe uneeded. --- src/qt/CMakeLists.txt | 9 +- src/qt/qt_gleswidget.hpp | 71 ------------ src/qt/qt_hardwarerenderer.cpp | 44 ++++++++ src/qt/qt_hardwarerenderer.hpp | 55 +++++++++ src/qt/qt_mainwindow.cpp | 34 ++++-- src/qt/qt_mainwindow.hpp | 4 + src/qt/qt_mainwindow.ui | 43 +++++++- ...qt_gleswidget.cpp => qt_rendererstack.cpp} | 104 ++++++++++-------- src/qt/qt_rendererstack.hpp | 60 ++++++++++ src/qt/qt_rendererstack.ui | 19 ++++ src/qt/qt_softwarerenderer.cpp | 22 ++++ src/qt/qt_softwarerenderer.hpp | 22 ++++ 12 files changed, 352 insertions(+), 135 deletions(-) delete mode 100644 src/qt/qt_gleswidget.hpp create mode 100644 src/qt/qt_hardwarerenderer.cpp create mode 100644 src/qt/qt_hardwarerenderer.hpp rename src/qt/{qt_gleswidget.cpp => qt_rendererstack.cpp} (55%) create mode 100644 src/qt/qt_rendererstack.hpp create mode 100644 src/qt/qt_rendererstack.ui create mode 100644 src/qt/qt_softwarerenderer.cpp create mode 100644 src/qt/qt_softwarerenderer.hpp 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