Qt: Add optional frame counter to OSD (closes #1728)

This commit is contained in:
Vicki Pfau 2022-01-15 20:24:34 -08:00
parent b512d6d455
commit 006dba7d69
13 changed files with 115 additions and 17 deletions

View File

@ -33,6 +33,7 @@ Misc:
- Qt: Only set default controller bindings if loading fails (fixes mgba.io/i/799) - Qt: Only set default controller bindings if loading fails (fixes mgba.io/i/799)
- Qt: Save converter now supports importing GameShark Advance saves - Qt: Save converter now supports importing GameShark Advance saves
- Qt: Save positions of multiplayer windows (closes mgba.io/i/2128) - Qt: Save positions of multiplayer windows (closes mgba.io/i/2128)
- Qt: Add optional frame counter to OSD (closes mgba.io/i/1728)
- Windows: Attach to console if present - Windows: Attach to console if present
0.9.3: (2021-12-17) 0.9.3: (2021-12-17)

View File

@ -86,6 +86,7 @@ CoreController::CoreController(mCore* core, QObject* parent)
} }
controller->m_resetActions.clear(); controller->m_resetActions.clear();
controller->m_frameCounter = -1;
if (!controller->m_hwaccel) { if (!controller->m_hwaccel) {
context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer.data()), controller->screenDimensions().width()); context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer.data()), controller->screenDimensions().width());
@ -1081,6 +1082,7 @@ void CoreController::finishFrame() {
mCoreThreadPauseFromThread(&m_threadContext); mCoreThreadPauseFromThread(&m_threadContext);
} }
} }
++m_frameCounter;
} }
updateKeys(); updateKeys();

View File

@ -125,6 +125,7 @@ public:
bool videoSync() const { return m_videoSync; } bool videoSync() const { return m_videoSync; }
void addFrameAction(std::function<void ()> callback); void addFrameAction(std::function<void ()> callback);
uint64_t frameCounter() const { return m_frameCounter; }
public slots: public slots:
void start(); void start();
@ -244,6 +245,7 @@ private:
std::unique_ptr<mCacheSet> m_cacheSet; std::unique_ptr<mCacheSet> m_cacheSet;
std::unique_ptr<Override> m_override; std::unique_ptr<Override> m_override;
uint64_t m_frameCounter;
QList<std::function<void()>> m_resetActions; QList<std::function<void()>> m_resetActions;
QList<std::function<void()>> m_frameActions; QList<std::function<void()>> m_frameActions;
QMutex m_actionMutex{QMutex::Recursive}; QMutex m_actionMutex{QMutex::Recursive};

View File

@ -85,14 +85,20 @@ Display::Display(QWidget* parent)
} }
void Display::attach(std::shared_ptr<CoreController> controller) { void Display::attach(std::shared_ptr<CoreController> controller) {
connect(controller.get(), &CoreController::stateLoaded, this, &Display::resizeContext); CoreController* controllerP = controller.get();
connect(controller.get(), &CoreController::stateLoaded, this, &Display::forceDraw); connect(controllerP, &CoreController::stateLoaded, this, &Display::resizeContext);
connect(controller.get(), &CoreController::rewound, this, &Display::forceDraw); connect(controllerP, &CoreController::stateLoaded, this, &Display::forceDraw);
connect(controller.get(), &CoreController::paused, this, &Display::pauseDrawing); connect(controllerP, &CoreController::rewound, this, &Display::forceDraw);
connect(controller.get(), &CoreController::unpaused, this, &Display::unpauseDrawing); connect(controllerP, &CoreController::paused, this, &Display::pauseDrawing);
connect(controller.get(), &CoreController::frameAvailable, this, &Display::framePosted); connect(controllerP, &CoreController::unpaused, this, &Display::unpauseDrawing);
connect(controller.get(), &CoreController::statusPosted, this, &Display::showMessage); connect(controllerP, &CoreController::frameAvailable, this, &Display::framePosted);
connect(controller.get(), &CoreController::didReset, this, &Display::resizeContext); connect(controllerP, &CoreController::frameAvailable, this, [controllerP, this]() {
if (m_showFrameCounter) {
m_messagePainter.showFrameCounter(controllerP->frameCounter());
}
});
connect(controllerP, &CoreController::statusPosted, this, &Display::showMessage);
connect(controllerP, &CoreController::didReset, this, &Display::resizeContext);
} }
void Display::configure(ConfigController* config) { void Display::configure(ConfigController* config) {
@ -102,6 +108,7 @@ void Display::configure(ConfigController* config) {
interframeBlending(opts->interframeBlending); interframeBlending(opts->interframeBlending);
filter(opts->resampleVideo); filter(opts->resampleVideo);
config->updateOption("showOSD"); config->updateOption("showOSD");
config->updateOption("showFrameCounter");
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) #if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (opts->shader) { if (opts->shader) {
struct VDir* shader = VDirOpen(opts->shader); struct VDir* shader = VDirOpen(opts->shader);
@ -137,6 +144,13 @@ void Display::showOSDMessages(bool enable) {
m_showOSD = enable; m_showOSD = enable;
} }
void Display::showFrameCounter(bool enable) {
m_showFrameCounter = enable;
if (!enable) {
m_messagePainter.clearFrameCounter();
}
}
void Display::filter(bool filter) { void Display::filter(bool filter) {
m_filter = filter; m_filter = filter;
} }

View File

@ -42,6 +42,7 @@ public:
bool hasInterframeBlending() const { return m_interframeBlending; } bool hasInterframeBlending() const { return m_interframeBlending; }
bool isFiltered() const { return m_filter; } bool isFiltered() const { return m_filter; }
bool isShowOSD() const { return m_showOSD; } bool isShowOSD() const { return m_showOSD; }
bool isShowFrameCounter() const { return m_showFrameCounter; }
virtual void attach(std::shared_ptr<CoreController>); virtual void attach(std::shared_ptr<CoreController>);
virtual void configure(ConfigController*); virtual void configure(ConfigController*);
@ -69,6 +70,7 @@ public slots:
virtual void lockIntegerScaling(bool lock); virtual void lockIntegerScaling(bool lock);
virtual void interframeBlending(bool enable); virtual void interframeBlending(bool enable);
virtual void showOSDMessages(bool enable); virtual void showOSDMessages(bool enable);
virtual void showFrameCounter(bool enable);
virtual void filter(bool filter); virtual void filter(bool filter);
virtual void framePosted() = 0; virtual void framePosted() = 0;
virtual void setShaders(struct VDir*) = 0; virtual void setShaders(struct VDir*) = 0;
@ -89,6 +91,7 @@ private:
MessagePainter m_messagePainter; MessagePainter m_messagePainter;
bool m_showOSD = true; bool m_showOSD = true;
bool m_showFrameCounter = false;
bool m_lockAspectRatio = false; bool m_lockAspectRatio = false;
bool m_lockIntegerScaling = false; bool m_lockIntegerScaling = false;
bool m_interframeBlending = false; bool m_interframeBlending = false;

View File

@ -95,6 +95,7 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
lockIntegerScaling(isIntegerScalingLocked()); lockIntegerScaling(isIntegerScalingLocked());
interframeBlending(hasInterframeBlending()); interframeBlending(hasInterframeBlending());
showOSDMessages(isShowOSD()); showOSDMessages(isShowOSD());
showFrameCounter(isShowFrameCounter());
filter(isFiltered()); filter(isFiltered());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
@ -218,6 +219,11 @@ void DisplayGL::showOSDMessages(bool enable) {
QMetaObject::invokeMethod(m_painter.get(), "showOSD", Q_ARG(bool, enable)); QMetaObject::invokeMethod(m_painter.get(), "showOSD", Q_ARG(bool, enable));
} }
void DisplayGL::showFrameCounter(bool enable) {
Display::showFrameCounter(enable);
QMetaObject::invokeMethod(m_painter.get(), "showFrameCounter", Q_ARG(bool, enable));
}
void DisplayGL::filter(bool filter) { void DisplayGL::filter(bool filter) {
Display::filter(filter); Display::filter(filter);
QMetaObject::invokeMethod(m_painter.get(), "filter", Q_ARG(bool, filter)); QMetaObject::invokeMethod(m_painter.get(), "filter", Q_ARG(bool, filter));
@ -429,6 +435,10 @@ void PainterGL::showOSD(bool enable) {
m_showOSD = enable; m_showOSD = enable;
} }
void PainterGL::showFrameCounter(bool enable) {
m_showFrameCounter = enable;
}
void PainterGL::filter(bool filter) { void PainterGL::filter(bool filter) {
m_backend->filter = filter; m_backend->filter = filter;
if (m_started && !m_active) { if (m_started && !m_active) {

View File

@ -66,6 +66,7 @@ public slots:
void lockIntegerScaling(bool lock) override; void lockIntegerScaling(bool lock) override;
void interframeBlending(bool enable) override; void interframeBlending(bool enable) override;
void showOSDMessages(bool enable) override; void showOSDMessages(bool enable) override;
void showFrameCounter(bool enable) override;
void filter(bool filter) override; void filter(bool filter) override;
void framePosted() override; void framePosted() override;
void setShaders(struct VDir*) override; void setShaders(struct VDir*) override;
@ -122,6 +123,7 @@ public slots:
void lockIntegerScaling(bool lock); void lockIntegerScaling(bool lock);
void interframeBlending(bool enable); void interframeBlending(bool enable);
void showOSD(bool enable); void showOSD(bool enable);
void showFrameCounter(bool enable);
void filter(bool filter); void filter(bool filter);
void resizeContext(); void resizeContext();
@ -155,6 +157,7 @@ private:
CoreController::Interrupter m_interrupter; CoreController::Interrupter m_interrupter;
bool m_supportsShaders; bool m_supportsShaders;
bool m_showOSD; bool m_showOSD;
bool m_showFrameCounter;
VideoShader m_shader{}; VideoShader m_shader{};
VideoBackend* m_backend = nullptr; VideoBackend* m_backend = nullptr;
QSize m_size; QSize m_size;

View File

@ -106,7 +106,7 @@ void DisplayQt::paintEvent(QPaintEvent*) {
} }
painter.drawImage(full, m_backing, QRect(0, 0, m_width, m_height)); painter.drawImage(full, m_backing, QRect(0, 0, m_width, m_height));
painter.setOpacity(1); painter.setOpacity(1);
if (isShowOSD()) { if (isShowOSD() || isShowFrameCounter()) {
messagePainter()->paint(&painter); messagePainter()->paint(&painter);
} }
} }

View File

@ -18,6 +18,8 @@ MessagePainter::MessagePainter(QObject* parent)
{ {
m_messageFont = GBAApp::app()->monospaceFont(); m_messageFont = GBAApp::app()->monospaceFont();
m_messageFont.setPixelSize(13); m_messageFont.setPixelSize(13);
m_frameFont = GBAApp::app()->monospaceFont();
m_frameFont.setPixelSize(10);
connect(&m_messageTimer, &QTimer::timeout, this, &MessagePainter::clearMessage); connect(&m_messageTimer, &QTimer::timeout, this, &MessagePainter::clearMessage);
m_messageTimer.setSingleShot(true); m_messageTimer.setSingleShot(true);
m_messageTimer.setInterval(5000); m_messageTimer.setInterval(5000);
@ -34,6 +36,9 @@ void MessagePainter::resize(const QSize& size, qreal scaleFactor) {
m_world.scale(area / 220., area / 220.); m_world.scale(area / 220., area / 220.);
m_local = QPoint(area / 100., drawH - m_messageFont.pixelSize() * m_world.m22() * 1.3); m_local = QPoint(area / 100., drawH - m_messageFont.pixelSize() * m_world.m22() * 1.3);
QFontMetrics metrics(m_frameFont);
m_framePoint = QPoint(drawW / m_world.m11() - metrics.height() * 0.1, metrics.height() * 0.75);
m_mutex.lock(); m_mutex.lock();
redraw(); redraw();
m_mutex.unlock(); m_mutex.unlock();
@ -72,6 +77,24 @@ void MessagePainter::paint(QPainter* painter) {
if (!m_message.text().isEmpty()) { if (!m_message.text().isEmpty()) {
painter->drawPixmap(m_local, m_pixmap); painter->drawPixmap(m_local, m_pixmap);
} }
if (m_drawFrameCounter) {
QString frame(tr("Frame %1").arg(m_frameCounter));
QFontMetrics metrics(m_frameFont);
painter->setWorldTransform(m_world);
painter->setRenderHint(QPainter::Antialiasing);
painter->setFont(m_frameFont);
painter->setPen(Qt::black);
painter->translate(-metrics.width(frame), 0);
const static int ITERATIONS = 11;
for (int i = 0; i < ITERATIONS; ++i) {
painter->save();
painter->translate(cos(i * 2.0 * M_PI / ITERATIONS) * 0.8, sin(i * 2.0 * M_PI / ITERATIONS) * 0.8);
painter->drawText(m_framePoint, frame);
painter->restore();
}
painter->setPen(Qt::white);
painter->drawText(m_framePoint, frame);
}
} }
void MessagePainter::showMessage(const QString& message) { void MessagePainter::showMessage(const QString& message) {
@ -90,3 +113,16 @@ void MessagePainter::clearMessage() {
m_mutex.unlock(); m_mutex.unlock();
m_messageTimer.stop(); m_messageTimer.stop();
} }
void MessagePainter::showFrameCounter(uint64_t frameCounter) {
m_mutex.lock();
m_frameCounter = frameCounter;
m_drawFrameCounter = true;
m_mutex.unlock();
}
void MessagePainter::clearFrameCounter() {
m_mutex.lock();
m_drawFrameCounter = false;
m_mutex.unlock();
}

View File

@ -27,16 +27,26 @@ public slots:
void showMessage(const QString& message); void showMessage(const QString& message);
void clearMessage(); void clearMessage();
void showFrameCounter(uint64_t);
void clearFrameCounter();
private: private:
void redraw(); void redraw();
QMutex m_mutex; QMutex m_mutex;
QStaticText m_message; QStaticText m_message;
qreal m_scaleFactor = 1; qreal m_scaleFactor = 1;
uint64_t m_frameCounter;
bool m_drawFrameCounter = false;
QPoint m_local;
QPixmap m_pixmap; QPixmap m_pixmap;
QPixmap m_pixmapBuffer; QPixmap m_pixmapBuffer;
QPointF m_framePoint = QPointF(0, 0);
QFont m_frameFont;
QTimer m_messageTimer{this}; QTimer m_messageTimer{this};
QPoint m_local;
QTransform m_world; QTransform m_world;
QFont m_messageFont; QFont m_messageFont;
}; };

View File

@ -450,6 +450,7 @@ void SettingsView::updateConfig() {
saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling); saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
saveSetting("interframeBlending", m_ui.interframeBlending); saveSetting("interframeBlending", m_ui.interframeBlending);
saveSetting("showOSD", m_ui.showOSD); saveSetting("showOSD", m_ui.showOSD);
saveSetting("showFrameCounter", m_ui.showFrameCounter);
saveSetting("volume", m_ui.volume); saveSetting("volume", m_ui.volume);
saveSetting("mute", m_ui.mute); saveSetting("mute", m_ui.mute);
saveSetting("fastForwardVolume", m_ui.volumeFf); saveSetting("fastForwardVolume", m_ui.volumeFf);
@ -668,6 +669,7 @@ void SettingsView::reloadConfig() {
loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling); loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
loadSetting("interframeBlending", m_ui.interframeBlending); loadSetting("interframeBlending", m_ui.interframeBlending);
loadSetting("showOSD", m_ui.showOSD, true); loadSetting("showOSD", m_ui.showOSD, true);
loadSetting("showFrameCounter", m_ui.showFrameCounter);
loadSetting("volume", m_ui.volume, 0x100); loadSetting("volume", m_ui.volume, 0x100);
loadSetting("mute", m_ui.mute, false); loadSetting("mute", m_ui.mute, false);
loadSetting("fastForwardVolume", m_ui.volumeFf, m_ui.volume->value()); loadSetting("fastForwardVolume", m_ui.volumeFf, m_ui.volume->value());

View File

@ -667,20 +667,27 @@
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="16" column="1">
<widget class="QCheckBox" name="showFrameCounter">
<property name="text">
<string>Show frame count in OSD</string>
</property>
</widget>
</item>
<item row="17" column="1">
<widget class="QCheckBox" name="useDiscordPresence"> <widget class="QCheckBox" name="useDiscordPresence">
<property name="text"> <property name="text">
<string>Enable Discord Rich Presence</string> <string>Enable Discord Rich Presence</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="0" colspan="2"> <item row="18" column="0" colspan="2">
<widget class="Line" name="line_13"> <widget class="Line" name="line_13">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="1"> <item row="19" column="1">
<widget class="QCheckBox" name="autosave"> <widget class="QCheckBox" name="autosave">
<property name="text"> <property name="text">
<string>Automatically save state</string> <string>Automatically save state</string>
@ -690,7 +697,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="1"> <item row="20" column="1">
<widget class="QCheckBox" name="autoload"> <widget class="QCheckBox" name="autoload">
<property name="text"> <property name="text">
<string>Automatically load state</string> <string>Automatically load state</string>
@ -700,14 +707,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="20" column="0" colspan="2"> <item row="21" column="0" colspan="2">
<widget class="Line" name="line_16"> <widget class="Line" name="line_16">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="21" column="1"> <item row="22" column="1">
<widget class="QCheckBox" name="cheatAutosave"> <widget class="QCheckBox" name="cheatAutosave">
<property name="text"> <property name="text">
<string>Automatically save cheats</string> <string>Automatically save cheats</string>
@ -717,7 +724,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="22" column="1"> <item row="23" column="1">
<widget class="QCheckBox" name="cheatAutoload"> <widget class="QCheckBox" name="cheatAutoload">
<property name="text"> <property name="text">
<string>Automatically load cheats</string> <string>Automatically load cheats</string>

View File

@ -835,7 +835,6 @@ void Window::gameStarted() {
m_config->updateOption("lockAspectRatio"); m_config->updateOption("lockAspectRatio");
m_config->updateOption("interframeBlending"); m_config->updateOption("interframeBlending");
m_config->updateOption("resampleVideo"); m_config->updateOption("resampleVideo");
m_config->updateOption("showOSD");
if (m_savedScale > 0) { if (m_savedScale > 0) {
resizeFrame(size * m_savedScale); resizeFrame(size * m_savedScale);
} }
@ -1706,6 +1705,13 @@ void Window::setupMenu(QMenuBar* menubar) {
} }
}, this); }, this);
ConfigOption* showFrameCounter = m_config->addOption("showFrameCounter");
showFrameCounter->connect([this](const QVariant& value) {
if (m_display) {
m_display->showFrameCounter(value.toBool());
}
}, this);
ConfigOption* videoScale = m_config->addOption("videoScale"); ConfigOption* videoScale = m_config->addOption("videoScale");
videoScale->connect([this](const QVariant& value) { videoScale->connect([this](const QVariant& value) {
if (m_display) { if (m_display) {
@ -2004,6 +2010,8 @@ void Window::setController(CoreController* controller, const QString& fname) {
attachDisplay(); attachDisplay();
m_controller->loadConfig(m_config); m_controller->loadConfig(m_config);
m_config->updateOption("showOSD");
m_config->updateOption("showFrameCounter");
m_controller->start(); m_controller->start();
if (!m_pendingState.isEmpty()) { if (!m_pendingState.isEmpty()) {