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.
This commit is contained in:
Joakim L. Gilje
2021-12-04 21:33:04 +01:00
parent 5870e50022
commit a74afc3f1e
12 changed files with 352 additions and 135 deletions

View File

@@ -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

View File

@@ -1,71 +0,0 @@
#pragma once
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QPainter>
#include <QEvent>
#include <QKeyEvent>
#include <atomic>
#include <mutex>
#include <QApplication>
#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;
};

View File

@@ -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();
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QPainter>
#include <QEvent>
#include <QKeyEvent>
#include <atomic>
#include <mutex>
#include <QApplication>
#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;
};

View File

@@ -22,7 +22,6 @@ extern "C" {
#include <array>
#include "qt_settings.hpp"
#include "qt_gleswidget.hpp"
#include "qt_machinestatus.hpp"
#include "qt_mediamenu.hpp"
@@ -31,7 +30,6 @@ extern "C" {
#include <X11/keysym.h>
#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);
}

View File

@@ -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<MachineStatus> status;

View File

@@ -37,7 +37,14 @@
<number>0</number>
</property>
<item>
<widget class="GLESWidget" name="glesWidget"/>
<widget class="RendererStack" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="SoftwareRenderer" name="sw"/>
<widget class="HardwareRenderer" name="ogl"/>
<widget class="HardwareRenderer" name="gles"/>
</widget>
</item>
</layout>
</widget>
@@ -77,6 +84,9 @@
<string>View</string>
</property>
<addaction name="actionFullscreen"/>
<addaction name="actionSoftware_Renderer"/>
<addaction name="actionHardware_Renderer_OpenGL"/>
<addaction name="actionHardware_Renderer_OpenGL_ES"/>
</widget>
<widget class="QMenu" name="menuMedia">
<property name="title">
@@ -146,12 +156,39 @@
<string>Ctrl+Alt+PgUp</string>
</property>
</action>
<action name="actionSoftware_Renderer">
<property name="text">
<string>Software Renderer</string>
</property>
</action>
<action name="actionHardware_Renderer_OpenGL">
<property name="text">
<string>Hardware Renderer (OpenGL)</string>
</property>
</action>
<action name="actionHardware_Renderer_OpenGL_ES">
<property name="text">
<string>Hardware Renderer (OpenGL ES)</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>GLESWidget</class>
<class>HardwareRenderer</class>
<extends>QOpenGLWidget</extends>
<header>qt_gleswidget.hpp</header>
<header>qt_hardwarerenderer.hpp</header>
</customwidget>
<customwidget>
<class>SoftwareRenderer</class>
<extends>QWidget</extends>
<header>qt_softwarerenderer.hpp</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RendererStack</class>
<extends>QStackedWidget</extends>
<header>qt_rendererstack.hpp</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>

View File

@@ -1,8 +1,9 @@
#include <QApplication>
#include <QImage>
#include <QGuiApplication>
#include <qnamespace.h>
#include "qt_gleswidget.hpp"
#include "qt_rendererstack.hpp"
#include "ui_qt_rendererstack.h"
#include "qt_softwarerenderer.hpp"
#include "qt_hardwarerenderer.hpp"
#ifdef __APPLE__
#include <CoreGraphics/CoreGraphics.h>
#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<QImage>(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<void (GLESWidget::*)()>(&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<SoftwareRenderer*>(currentWidget()), &SoftwareRenderer::onBlit);
break;
case 1:
case 2:
connect(this, &RendererStack::blitToRenderer, dynamic_cast<HardwareRenderer*>(currentWidget()), &HardwareRenderer::onBlit);
break;
}
reqUpdate();
}

View File

@@ -0,0 +1,60 @@
#ifndef QT_RENDERERCONTAINER_HPP
#define QT_RENDERERCONTAINER_HPP
#include <QStackedWidget>
#include <QKeyEvent>
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<QImage> imagebufs;
};
#endif // QT_RENDERERCONTAINER_HPP

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RendererStack</class>
<widget class="QStackedWidget" name="RendererStack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>StackedWidget</string>
</property>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,22 @@
#include "qt_softwarerenderer.hpp"
#include <QPainter>
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();
}

View File

@@ -0,0 +1,22 @@
#ifndef SOFTWARERENDERER_HPP
#define SOFTWARERENDERER_HPP
#include <QWidget>
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