Skip to content

Commit

Permalink
Implement frameskip in renderer, calculate Actual FPS for all display…
Browse files Browse the repository at this point in the history
… modes, improve speed throttling and display
  • Loading branch information
calc84maniac committed Jun 15, 2024
1 parent 909b25f commit 299c476
Show file tree
Hide file tree
Showing 14 changed files with 124 additions and 52 deletions.
10 changes: 6 additions & 4 deletions core/lcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ static inline uint32_t lcd_bgr888out(uint32_t bgr888) {
return lcd_abgr8888out(UINT32_C(0xFF000000) | (bgr888 & 0xF8FCF8));
}

void emu_set_lcd_callback(void (*callback)(void*), void *data) {
void emu_set_lcd_callback(bool (*callback)(void*), void *data) {
lcd.gui_callback = callback;
lcd.gui_callback_data = data;
}
Expand Down Expand Up @@ -159,10 +159,11 @@ void emu_lcd_drawmem(void *output, void *data, void *data_end, uint32_t lcd_cont
while (out < out_end) { *out++ = 0xFF000000 | (unsigned int)(bus_rand() << (bus_rand() & 0x18)); }
}

void lcd_gui_event(void) {
bool lcd_gui_event(void) {
if (lcd.gui_callback) {
lcd.gui_callback(lcd.gui_callback_data);
return lcd.gui_callback(lcd.gui_callback_data);
}
return true;
}

void lcd_free(void) {
Expand Down Expand Up @@ -353,7 +354,8 @@ static void lcd_event(enum sched_item_id id) {
panel_vsync();
sched_repeat_relative(SCHED_LCD_DMA, SCHED_LCD, duration, 0);
} else {
lcd_gui_event();
(void)lcd_gui_event();
panel.skipFrame = true;
}
lcd.compare = LCD_LNBU;
break;
Expand Down
6 changes: 3 additions & 3 deletions core/lcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ typedef struct lcd_state {

/* Everything after here persists through reset! */
int useDma;
void (*gui_callback)(void*);
bool (*gui_callback)(void*);
void *gui_callback_data;
} lcd_state_t;

Expand All @@ -82,11 +82,11 @@ bool lcd_restore(FILE *image);
bool lcd_save(FILE *image);
void lcd_update(void);
void lcd_disable(void);
void lcd_gui_event(void);
bool lcd_gui_event(void);

/* api functions */
void emu_lcd_drawframe(void *output);
void emu_set_lcd_callback(void (*callback)(void*), void *data);
void emu_set_lcd_callback(bool (*callback)(void*), void *data);
void emu_set_lcd_dma(int enable);
void emu_set_lcd_gamma(int enable);

Expand Down
12 changes: 9 additions & 3 deletions core/panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,12 @@ static bool panel_start_line(uint16_t row) {
return false;
}
return true;
} else if (unlikely(panel.skipFrame)) {
panel.nextRamCol = PANEL_NUM_COLS;
panel.srcRow = panel.dstRow = -1;
return true;
}

/* Partial mode */
if (unlikely(panel.mode & PANEL_MODE_PARTIAL) &&
panel.partialStart > panel.partialEnd ?
Expand Down Expand Up @@ -527,8 +532,6 @@ static uint32_t panel_start_frame() {
sched_clear(SCHED_PANEL);
}

lcd_gui_event();

if ((panel.mode ^ panel.pendingMode) & PANEL_MODE_IDLE) {
panel.gammaDirty = true;
}
Expand Down Expand Up @@ -584,6 +587,9 @@ static uint32_t panel_start_frame() {
uint32_t ticksPerFrame = (horizBackPorch + panel.ticksPerLine) * (vertBackPorch + panel.linesPerFrame);
panel.lineStartTick = ticksPerFrame + panel.ticksPerLine;
panel_start_line(-vertBackPorch);

panel.skipFrame = !lcd_gui_event();

return ticksPerFrame;
}

Expand Down Expand Up @@ -903,7 +909,7 @@ static void panel_update_rgb_clock_method(void) {
if (!panel.params.RAMCTRL.RM) {
panel.clock_pixel = panel_clock_pixel_ignore;
} else if (panel_ram_bypass_enabled()) {
if (!(panel.mode & (PANEL_MODE_SLEEP | PANEL_MODE_OFF)) && panel.displayMode == PANEL_DM_RGB && panel.row < PANEL_NUM_ROWS) {
if (!(panel.mode & (PANEL_MODE_SLEEP | PANEL_MODE_OFF)) && panel.displayMode == PANEL_DM_RGB && panel.row < PANEL_NUM_ROWS && !panel.skipFrame) {
panel.clock_pixel = panel_clock_pixel_ram_bypass;
} else {
panel.clock_pixel = panel_clock_pixel_ignore;
Expand Down
1 change: 1 addition & 0 deletions core/panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ typedef struct panel_state {
uint8_t blankLevel;
bool accurateGamma;
bool gammaDirty;
bool skipFrame;
} panel_state_t;

extern panel_state_t panel;
Expand Down
4 changes: 4 additions & 0 deletions core/schedule.c
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,10 @@ uint32_t sched_get_clock_rate(enum clock_id clock) {
return (uint32_t)div_round(SCHED_BASE_CLOCK_RATE, &sched.clocks[clock]);
}

double sched_get_clock_rate_precise(enum clock_id clock) {
return (double)SCHED_BASE_CLOCK_RATE / sched.clocks[clock].tick_unit;
}

void sched_reset(void) {
const uint32_t def_rates[CLOCK_NUM_ITEMS] = { 48000000, 10000000, 60, 48000000, 24000000, 12000000, 6000000, 3000000, 1000000, 32768 };

Expand Down
1 change: 1 addition & 0 deletions core/schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ uint64_t sched_ticks_remaining(enum sched_item_id id);
uint64_t sched_ticks_remaining_relative(enum sched_item_id id, enum sched_item_id base, uint32_t offset);
bool sched_set_clock(enum clock_id clock, uint32_t new_rate);
uint32_t sched_get_clock_rate(enum clock_id clock);
double sched_get_clock_rate_precise(enum clock_id clock);
uint64_t sched_total_cycles(void);
uint64_t sched_total_time(enum clock_id clock);
uint64_t event_next_cycle(enum sched_item_id id);
Expand Down
3 changes: 3 additions & 0 deletions gui/qt/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ void MainWindow::debugRaise() {
connect(m_shortcutStepOver, &QShortcut::activated, this, &MainWindow::stepOver);
connect(m_shortcutStepNext, &QShortcut::activated, this, &MainWindow::stepNext);
connect(m_shortcutStepOut, &QShortcut::activated, this, &MainWindow::stepOut);
ui->lcd->disableBlend();
}

void MainWindow::debugExecute(uint32_t offset, uint8_t cmd) {
Expand Down Expand Up @@ -396,6 +397,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) {
debug.stepBasic = false;
debug.stepBasicNext = false;
prevReason = reason;
ui->lcd->disableBlend();
return;
}
}
Expand Down Expand Up @@ -499,6 +501,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) {
return;
case DBG_BASIC_USER:
debugBasicRaise();
ui->lcd->disableBlend();
return;
}

Expand Down
51 changes: 32 additions & 19 deletions gui/qt/emuthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ EmuThread::EmuThread(QObject *parent) : QThread{parent}, write{CONSOLE_BUFFER_SI
m_debug{false} {
assert(emu == nullptr);
emu = this;
std::fill(m_perfArray, m_perfArray + PerfArraySize, m_lastTime);
}

void EmuThread::run() {
Expand Down Expand Up @@ -126,8 +127,6 @@ void EmuThread::writeConsole(int console, const char *format, va_list args) {
}

void EmuThread::doStuff() {
const std::chrono::steady_clock::time_point cur_time = std::chrono::steady_clock::now();

while (!m_reqQueue.isEmpty()) {
int req = m_reqQueue.dequeue();
switch (+req) {
Expand Down Expand Up @@ -179,8 +178,6 @@ void EmuThread::doStuff() {
m_keyQueue.dequeue();
}
}

m_lastTime += std::chrono::steady_clock::now() - cur_time;
}

void EmuThread::throttleWait() {
Expand All @@ -189,29 +186,42 @@ void EmuThread::throttleWait() {
{
std::unique_lock<std::mutex> lockSpeed(m_mutexSpeed);
speed = m_speed;
if (!speed) {
sendSpeed(0);
m_cvSpeed.wait(lockSpeed, [this] { return m_speed != 0; });
throttle = m_throttle;
if (!speed && throttle) {
emit sendSpeed(0);
m_cvSpeed.wait(lockSpeed, [this] { return m_speed != 0 || !m_throttle; });
speed = m_speed;
throttle = m_throttle;
m_lastTime = std::chrono::steady_clock::now();
}
throttle = m_throttle;
}
std::chrono::duration<int, std::ratio<100, 60>> unit(1);
std::chrono::steady_clock::duration interval(std::chrono::duration_cast<std::chrono::steady_clock::duration>
(std::chrono::duration<int, std::ratio<1, 60 * 1000000>>(1000000 * 100 / speed)));
std::chrono::steady_clock::time_point cur_time = std::chrono::steady_clock::now(), next_time = m_lastTime + interval;
if (throttle && cur_time < next_time) {
sendSpeed(speed);
m_lastTime = next_time;
std::this_thread::sleep_until(next_time);
double run_rate = sched_get_clock_rate_precise(CLOCK_RUN);
std::chrono::steady_clock::time_point cur_time = std::chrono::steady_clock::now();
if (!throttle) {
m_lastTime = cur_time;
std::this_thread::yield();
} else {
if (m_lastTime != cur_time) {
sendSpeed(unit / (cur_time - m_lastTime));
std::chrono::steady_clock::duration interval(std::chrono::duration_cast<std::chrono::steady_clock::duration>
(std::chrono::duration<double>(100 / (speed * run_rate))));
std::chrono::steady_clock::time_point next_time = m_lastTime + interval;
std::chrono::steady_clock::time_point tolerance_time = m_lastTime + std::chrono::milliseconds(30);
if (cur_time < std::max(next_time, tolerance_time)) {
m_lastTime = next_time;
if (cur_time < next_time) {
std::this_thread::sleep_until(next_time);
}
} else {
m_lastTime = cur_time;
std::this_thread::yield();
}
std::this_thread::yield();
}
std::chrono::steady_clock::time_point timeNUnitsAgo = m_perfArray[m_perfIndex];
m_perfArray[m_perfIndex] = m_lastTime;
if (++m_perfIndex == PerfArraySize) {
m_perfIndex = 0;
}
std::chrono::duration<double> diff = m_lastTime - timeNUnitsAgo;
emit sendSpeed(diff.count() * run_rate * (1.0 / PerfArraySize));
}

void EmuThread::unblock() {
Expand Down Expand Up @@ -324,6 +334,9 @@ void EmuThread::setSpeed(int value) {
void EmuThread::setThrottle(bool state) {
std::unique_lock<std::mutex> lockSpeed(m_mutexSpeed);
m_throttle = state;
if (!state) {
m_cvSpeed.notify_one();
}
}

void EmuThread::setAsicRev(int rev) {
Expand Down
7 changes: 6 additions & 1 deletion gui/qt/emuthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class EmuThread : public QThread {
void debugCommand(int reason, uint32_t addr);

// speed
void sendSpeed(int value);
void sendSpeed(double value);

// state
void sendAsicRevInfo(const QList<int>& supportedRevs, int loadedRev, int defaultRev, bool python);
Expand Down Expand Up @@ -112,6 +112,8 @@ public slots:
m_cv.wait(lock);
}

static constexpr size_t PerfArraySize = 20;

QQueue<int> m_reqQueue;
emu_data_t m_saveType;
emu_data_t m_loadType;
Expand Down Expand Up @@ -143,6 +145,9 @@ public slots:

QQueue<quint16> m_keyQueue;
QMutex m_keyQueueMutex;

std::chrono::steady_clock::time_point m_perfArray[PerfArraySize];
size_t m_perfIndex = 0;
};

#endif
41 changes: 34 additions & 7 deletions gui/qt/lcdwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
#include "../../core/backlight.h"
#include "../../core/control.h"

#include <QtCore/QMetaObject>
#include <QtCore/QMetaMethod>
#include <QtGui/QPainter>
#include <QtGui/QMouseEvent>
#include <QtGui/QDrag>
#include <QtWidgets/QMenu>
#include <QtWidgets/QApplication>
#include <math.h>

LCDWidget::LCDWidget(QWidget *parent) : QWidget{parent} {
installEventFilter(keypadBridge);
Expand Down Expand Up @@ -142,12 +145,15 @@ void LCDWidget::mouseMoveEvent(QMouseEvent *e) {
}

double LCDWidget::refresh() {
unsigned int msNFramesAgo = m_array[m_index];
m_array[m_index] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
double guiFps = (1e3*ArraySize) / (m_array[m_index] - msNFramesAgo);
m_index = (m_index + 1) % ArraySize;
std::chrono::steady_clock::time_point timeNFramesAgo = m_perfArray[m_perfIndex];
m_perfArray[m_perfIndex] = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = m_perfArray[m_perfIndex] - timeNFramesAgo;
double guiFrameTime = diff.count() / PerfArraySize;
if (++m_perfIndex == PerfArraySize) {
m_perfIndex = 0;
}
update();
return guiFps;
return guiFrameTime;
}

void LCDWidget::setMain() {
Expand All @@ -156,7 +162,7 @@ void LCDWidget::setMain() {
m_blendedFrame.fill(Qt::black);
m_currFrame = &m_renderedFrame;
m_mutex.unlock();
emu_set_lcd_callback([](void *lcd) { reinterpret_cast<LCDWidget*>(lcd)->draw(); }, this);
emu_set_lcd_callback([](void *lcd) { return static_cast<LCDWidget*>(lcd)->draw(); }, this);
}

void LCDWidget::setResponseMode(bool state) {
Expand All @@ -168,10 +174,21 @@ void LCDWidget::setFrameskip(int value) {
m_skip = value;
}

void LCDWidget::disableBlend() {
if (m_currFrame == &m_blendedFrame) {
m_mutex.lock();
m_currFrame = &m_renderedFrame;
m_mutex.unlock();
static QMetaMethod updateMethod = staticMetaObject.method(staticMetaObject.indexOfSlot("update()"));
updateMethod.invoke(this);
}
}

// called by the emu thread to draw the lcd
void LCDWidget::draw() {
bool LCDWidget::draw() {
if (m_skip) {
m_skip--;
disableBlend();
} else {
m_skip = m_frameskip;
m_mutex.lock();
Expand Down Expand Up @@ -202,7 +219,17 @@ void LCDWidget::draw() {
apng_add_frame(m_currFrame->constBits());
#endif
double guiFps = 24e6 / (lcd.PCD * (lcd.HSW + lcd.HBP + lcd.CPL + lcd.HFP) * (lcd.VSW + lcd.VBP + lcd.LPP + lcd.VFP));
if (lcd.useDma && panel.displayMode != PANEL_DM_RGB && panel.lineStartTick != 0) {
double internalFps = sched_get_clock_rate_precise(CLOCK_PANEL) / panel.lineStartTick;
if (panel.displayMode != PANEL_DM_VSYNC) {
guiFps = internalFps;
} else if (unlikely(internalFps < guiFps)) {
guiFps /= ceil(guiFps / internalFps);
}
}
emit updateLcd(guiFps / (m_frameskip + 1));
}
// return whether next frame should be rendered
return m_skip == 0;
}

11 changes: 5 additions & 6 deletions gui/qt/lcdwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ class LCDWidget : public QWidget {
explicit LCDWidget(QWidget *p = Q_NULLPTR);
QImage getImage();
void setMain();
void draw();
bool draw();
void setFrameskip(int value);
void setResponseMode(bool state);
void disableBlend();

signals:
void updateLcd(double emuFps);
Expand All @@ -43,9 +44,7 @@ public slots:
LcdRight
};

enum {
ArraySize = 50
};
static constexpr size_t PerfArraySize = 30;

unsigned int m_side;
bool m_transferDrag = false;
Expand All @@ -61,8 +60,8 @@ public slots:
QString m_dragRom;
bool m_isSendingRom;

unsigned int m_array[ArraySize];
int m_index = 0;
std::chrono::steady_clock::time_point m_perfArray[PerfArraySize];
size_t m_perfIndex = 0;
bool m_responseMode = false;
int m_skip = 0;
int m_frameskip = 0;
Expand Down
Loading

0 comments on commit 299c476

Please sign in to comment.