perf: sync native danmaku with screen refresh
This commit is contained in:
@@ -12,7 +12,9 @@
|
|||||||
#include <QRectF>
|
#include <QRectF>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
|
#include <QShowEvent>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QWindow>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -26,7 +28,9 @@ constexpr qreal kReferenceHeight = 1080.0;
|
|||||||
constexpr qreal kTopMargin = 24.0;
|
constexpr qreal kTopMargin = 24.0;
|
||||||
constexpr qreal kBottomMargin = 48.0;
|
constexpr qreal kBottomMargin = 48.0;
|
||||||
constexpr int kResizeRebuildDebounceMs = 48;
|
constexpr int kResizeRebuildDebounceMs = 48;
|
||||||
constexpr int kDefaultFrameIntervalMs = 8;
|
constexpr int kFallbackFrameIntervalMs = 16;
|
||||||
|
constexpr int kMinFrameIntervalMs = 4;
|
||||||
|
constexpr int kMaxFrameIntervalMs = 33;
|
||||||
constexpr qreal kScrollStartPadding = 32.0;
|
constexpr qreal kScrollStartPadding = 32.0;
|
||||||
constexpr qreal kScrollEndPadding = 48.0;
|
constexpr qreal kScrollEndPadding = 48.0;
|
||||||
constexpr qreal kScrollLaneGapPadding = 96.0;
|
constexpr qreal kScrollLaneGapPadding = 96.0;
|
||||||
@@ -150,7 +154,7 @@ NativeDanmakuOverlay::NativeDanmakuOverlay(MpvController *controller,
|
|||||||
|
|
||||||
m_frameTimer = new QTimer(this);
|
m_frameTimer = new QTimer(this);
|
||||||
m_frameTimer->setTimerType(Qt::PreciseTimer);
|
m_frameTimer->setTimerType(Qt::PreciseTimer);
|
||||||
m_frameTimer->setInterval(kDefaultFrameIntervalMs);
|
m_frameTimer->setInterval(kFallbackFrameIntervalMs);
|
||||||
connect(m_frameTimer, &QTimer::timeout, this, [this]() {
|
connect(m_frameTimer, &QTimer::timeout, this, [this]() {
|
||||||
const qint64 positionMs = currentPositionMs();
|
const qint64 positionMs = currentPositionMs();
|
||||||
if (m_lastResolvedPositionMs < 0 || positionMs < m_lastResolvedPositionMs ||
|
if (m_lastResolvedPositionMs < 0 || positionMs < m_lastResolvedPositionMs ||
|
||||||
@@ -168,6 +172,8 @@ NativeDanmakuOverlay::NativeDanmakuOverlay(MpvController *controller,
|
|||||||
connect(m_scheduleRebuildTimer, &QTimer::timeout, this,
|
connect(m_scheduleRebuildTimer, &QTimer::timeout, this,
|
||||||
&NativeDanmakuOverlay::rebuildScheduleNow);
|
&NativeDanmakuOverlay::rebuildScheduleNow);
|
||||||
|
|
||||||
|
updateScreenRefreshBinding();
|
||||||
|
|
||||||
if (!m_controller) {
|
if (!m_controller) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -331,6 +337,13 @@ void NativeDanmakuOverlay::resizeEvent(QResizeEvent *event)
|
|||||||
{
|
{
|
||||||
QWidget::resizeEvent(event);
|
QWidget::resizeEvent(event);
|
||||||
requestScheduleRebuild(false, false);
|
requestScheduleRebuild(false, false);
|
||||||
|
updateScreenRefreshBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeDanmakuOverlay::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
updateScreenRefreshBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeDanmakuOverlay::rebuildSchedule()
|
void NativeDanmakuOverlay::rebuildSchedule()
|
||||||
@@ -653,20 +666,76 @@ void NativeDanmakuOverlay::syncFrameTimer()
|
|||||||
|
|
||||||
int NativeDanmakuOverlay::targetFrameIntervalMs() const
|
int NativeDanmakuOverlay::targetFrameIntervalMs() const
|
||||||
{
|
{
|
||||||
const QScreen *targetScreen = screen();
|
const QScreen *screen = targetScreen();
|
||||||
if (!targetScreen && window()) {
|
const qreal refreshRate = screen ? screen->refreshRate() : 0.0;
|
||||||
targetScreen = window()->screen();
|
|
||||||
}
|
|
||||||
|
|
||||||
const qreal refreshRate = targetScreen ? targetScreen->refreshRate() : 0.0;
|
|
||||||
if (refreshRate <= 0.0) {
|
if (refreshRate <= 0.0) {
|
||||||
return kDefaultFrameIntervalMs;
|
return kFallbackFrameIntervalMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
const qreal desiredRefreshRate = std::max<qreal>(120.0, refreshRate);
|
return qBound(kMinFrameIntervalMs,
|
||||||
return qBound(4,
|
static_cast<int>(std::lround(1000.0 / refreshRate)),
|
||||||
static_cast<int>(std::lround(1000.0 / desiredRefreshRate)),
|
kMaxFrameIntervalMs);
|
||||||
kDefaultFrameIntervalMs);
|
}
|
||||||
|
|
||||||
|
QScreen *NativeDanmakuOverlay::targetScreen() const
|
||||||
|
{
|
||||||
|
if (QScreen *widgetScreen = screen()) {
|
||||||
|
return widgetScreen;
|
||||||
|
}
|
||||||
|
if (const QWidget *topLevel = window()) {
|
||||||
|
if (QWindow *handle = topLevel->windowHandle()) {
|
||||||
|
if (QScreen *windowScreen = handle->screen()) {
|
||||||
|
return windowScreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (QScreen *windowScreen = topLevel->screen()) {
|
||||||
|
return windowScreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeDanmakuOverlay::updateScreenRefreshBinding()
|
||||||
|
{
|
||||||
|
QWindow *currentWindow = nullptr;
|
||||||
|
if (QWidget *topLevel = window()) {
|
||||||
|
currentWindow = topLevel->windowHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_observedWindow != currentWindow) {
|
||||||
|
if (m_windowScreenConnection) {
|
||||||
|
QObject::disconnect(m_windowScreenConnection);
|
||||||
|
m_windowScreenConnection = {};
|
||||||
|
}
|
||||||
|
m_observedWindow = currentWindow;
|
||||||
|
if (m_observedWindow) {
|
||||||
|
m_windowScreenConnection =
|
||||||
|
connect(m_observedWindow, &QWindow::screenChanged, this,
|
||||||
|
[this](QScreen *) {
|
||||||
|
updateScreenRefreshBinding();
|
||||||
|
syncFrameTimer();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QScreen *currentScreen = targetScreen();
|
||||||
|
if (m_observedScreen == currentScreen) {
|
||||||
|
syncFrameTimer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_screenRefreshConnection) {
|
||||||
|
QObject::disconnect(m_screenRefreshConnection);
|
||||||
|
m_screenRefreshConnection = {};
|
||||||
|
}
|
||||||
|
m_observedScreen = currentScreen;
|
||||||
|
if (m_observedScreen) {
|
||||||
|
m_screenRefreshConnection =
|
||||||
|
connect(m_observedScreen, &QScreen::refreshRateChanged, this,
|
||||||
|
[this](qreal) { syncFrameTimer(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
syncFrameTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeDanmakuOverlay::refreshBasePosition(qint64 positionMs)
|
void NativeDanmakuOverlay::refreshBasePosition(qint64 positionMs)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QMetaObject>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
@@ -14,9 +15,12 @@
|
|||||||
class MpvController;
|
class MpvController;
|
||||||
class QPaintEvent;
|
class QPaintEvent;
|
||||||
class QResizeEvent;
|
class QResizeEvent;
|
||||||
|
class QScreen;
|
||||||
|
class QShowEvent;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class QVariant;
|
class QVariant;
|
||||||
class QRectF;
|
class QRectF;
|
||||||
|
class QWindow;
|
||||||
|
|
||||||
class NativeDanmakuOverlay : public QWidget
|
class NativeDanmakuOverlay : public QWidget
|
||||||
{
|
{
|
||||||
@@ -35,6 +39,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class ItemType {
|
enum class ItemType {
|
||||||
@@ -66,6 +71,8 @@ private:
|
|||||||
void clearScheduledState(bool clearSpriteCache = false);
|
void clearScheduledState(bool clearSpriteCache = false);
|
||||||
void syncFrameTimer();
|
void syncFrameTimer();
|
||||||
int targetFrameIntervalMs() const;
|
int targetFrameIntervalMs() const;
|
||||||
|
QScreen *targetScreen() const;
|
||||||
|
void updateScreenRefreshBinding();
|
||||||
void refreshBasePosition(qint64 positionMs);
|
void refreshBasePosition(qint64 positionMs);
|
||||||
qreal currentPositionMsPrecise() const;
|
qreal currentPositionMsPrecise() const;
|
||||||
qint64 currentPositionMs() const;
|
qint64 currentPositionMs() const;
|
||||||
@@ -80,6 +87,10 @@ private:
|
|||||||
QPointer<MpvController> m_controller;
|
QPointer<MpvController> m_controller;
|
||||||
QTimer *m_frameTimer = nullptr;
|
QTimer *m_frameTimer = nullptr;
|
||||||
QTimer *m_scheduleRebuildTimer = nullptr;
|
QTimer *m_scheduleRebuildTimer = nullptr;
|
||||||
|
QPointer<QWindow> m_observedWindow;
|
||||||
|
QPointer<QScreen> m_observedScreen;
|
||||||
|
QMetaObject::Connection m_windowScreenConnection;
|
||||||
|
QMetaObject::Connection m_screenRefreshConnection;
|
||||||
QElapsedTimer m_positionClock;
|
QElapsedTimer m_positionClock;
|
||||||
QList<DanmakuComment> m_comments;
|
QList<DanmakuComment> m_comments;
|
||||||
QList<DanmakuComment> m_sortedComments;
|
QList<DanmakuComment> m_sortedComments;
|
||||||
|
|||||||
Reference in New Issue
Block a user