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: Save converter now supports importing GameShark Advance saves
- 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
0.9.3: (2021-12-17)

View File

@ -86,6 +86,7 @@ CoreController::CoreController(mCore* core, QObject* parent)
}
controller->m_resetActions.clear();
controller->m_frameCounter = -1;
if (!controller->m_hwaccel) {
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);
}
}
++m_frameCounter;
}
updateKeys();

View File

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

View File

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

View File

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

View File

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

View File

@ -66,6 +66,7 @@ public slots:
void lockIntegerScaling(bool lock) override;
void interframeBlending(bool enable) override;
void showOSDMessages(bool enable) override;
void showFrameCounter(bool enable) override;
void filter(bool filter) override;
void framePosted() override;
void setShaders(struct VDir*) override;
@ -122,6 +123,7 @@ public slots:
void lockIntegerScaling(bool lock);
void interframeBlending(bool enable);
void showOSD(bool enable);
void showFrameCounter(bool enable);
void filter(bool filter);
void resizeContext();
@ -155,6 +157,7 @@ private:
CoreController::Interrupter m_interrupter;
bool m_supportsShaders;
bool m_showOSD;
bool m_showFrameCounter;
VideoShader m_shader{};
VideoBackend* m_backend = nullptr;
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.setOpacity(1);
if (isShowOSD()) {
if (isShowOSD() || isShowFrameCounter()) {
messagePainter()->paint(&painter);
}
}

View File

@ -18,6 +18,8 @@ MessagePainter::MessagePainter(QObject* parent)
{
m_messageFont = GBAApp::app()->monospaceFont();
m_messageFont.setPixelSize(13);
m_frameFont = GBAApp::app()->monospaceFont();
m_frameFont.setPixelSize(10);
connect(&m_messageTimer, &QTimer::timeout, this, &MessagePainter::clearMessage);
m_messageTimer.setSingleShot(true);
m_messageTimer.setInterval(5000);
@ -34,6 +36,9 @@ void MessagePainter::resize(const QSize& size, qreal scaleFactor) {
m_world.scale(area / 220., area / 220.);
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();
redraw();
m_mutex.unlock();
@ -72,6 +77,24 @@ void MessagePainter::paint(QPainter* painter) {
if (!m_message.text().isEmpty()) {
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) {
@ -90,3 +113,16 @@ void MessagePainter::clearMessage() {
m_mutex.unlock();
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 clearMessage();
void showFrameCounter(uint64_t);
void clearFrameCounter();
private:
void redraw();
QMutex m_mutex;
QStaticText m_message;
qreal m_scaleFactor = 1;
uint64_t m_frameCounter;
bool m_drawFrameCounter = false;
QPoint m_local;
QPixmap m_pixmap;
QPixmap m_pixmapBuffer;
QPointF m_framePoint = QPointF(0, 0);
QFont m_frameFont;
QTimer m_messageTimer{this};
QPoint m_local;
QTransform m_world;
QFont m_messageFont;
};

View File

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

View File

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

View File

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