1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-15 20:24:07 +00:00

Merge branch 'master' into LockFreeQueue

This commit is contained in:
Thomas Harte 2020-07-27 23:18:45 -04:00
commit 62be2a2eec
5 changed files with 48 additions and 17 deletions

View File

@ -63,6 +63,13 @@ class VSyncPredictor {
frame_duration_ = Nanos(1'000'000'000.0f / rate); frame_duration_ = Nanos(1'000'000'000.0f / rate);
} }
/*!
@returns The time this class currently believes a whole frame occupies.
*/
Time::Nanos frame_duration() {
return frame_duration_;
}
/*! /*!
Adds a record of how much jitter was experienced in scheduling; these values will be Adds a record of how much jitter was experienced in scheduling; these values will be
factored into the @c suggested_draw_time if supplied. factored into the @c suggested_draw_time if supplied.
@ -87,15 +94,13 @@ class VSyncPredictor {
(if those figures are being supplied). (if those figures are being supplied).
*/ */
Nanos suggested_draw_time() { Nanos suggested_draw_time() {
const auto mean = redraw_period_.mean() - timer_jitter_.mean() - vsync_jitter_.mean(); const auto mean = redraw_period_.mean() + timer_jitter_.mean() + vsync_jitter_.mean();
const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_jitter_.variance(); const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_jitter_.variance();
// Permit three standard deviations from the mean, to cover 99.9% of cases. // Permit three standard deviations from the mean, to cover 99.9% of cases.
const auto period = mean - Nanos(3.0f * sqrt(float(variance))); const auto period = mean + Nanos(3.0f * sqrt(float(variance)));
assert(abs(period) < 10'000'000'000); return last_vsync_ + frame_duration_ - period;
return last_vsync_ + period;
} }
private: private:
@ -109,7 +114,6 @@ class VSyncPredictor {
} }
void post(Time::Nanos value) { void post(Time::Nanos value) {
assert(abs(value) < 10'000'000'000); // 10 seconds is a very liberal maximum.
sum_ -= history_[write_pointer_]; sum_ -= history_[write_pointer_];
sum_ += value; sum_ += value;
history_[write_pointer_] = value; history_[write_pointer_] = value;

View File

@ -45,7 +45,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
MainWindow::MainWindow(const QString &fileName) { MainWindow::MainWindow(const QString &fileName) {
init(); init();
launchFile(fileName); if(!launchFile(fileName)) {
setUIPhase(UIPhase::SelectingMachine);
}
} }
void MainWindow::deleteMachine() { void MainWindow::deleteMachine() {
@ -210,11 +212,17 @@ void MainWindow::insertFile(const QString &fileName) {
mediaTarget->insert_media(media); mediaTarget->insert_media(media);
} }
void MainWindow::launchFile(const QString &fileName) { bool MainWindow::launchFile(const QString &fileName) {
targets = Analyser::Static::GetTargets(fileName.toStdString()); targets = Analyser::Static::GetTargets(fileName.toStdString());
if(!targets.empty()) { if(!targets.empty()) {
openFileName = QFileInfo(fileName).fileName(); openFileName = QFileInfo(fileName).fileName();
launchMachine(); launchMachine();
return true;
} else {
QMessageBox msgBox;
msgBox.setText("Unable to open file: " + fileName);
msgBox.exec();
return false;
} }
} }
@ -707,6 +715,7 @@ void MainWindow::dropEvent(QDropEvent* event) {
bool foundROM = false; bool foundROM = false;
const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString(); const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();
QString unusedRoms;
for(const auto &url: event->mimeData()->urls()) { for(const auto &url: event->mimeData()->urls()) {
const char *const name = url.toLocalFile().toUtf8(); const char *const name = url.toLocalFile().toUtf8();
FILE *const file = fopen(name, "rb"); FILE *const file = fopen(name, "rb");
@ -716,6 +725,7 @@ void MainWindow::dropEvent(QDropEvent* event) {
CRC::CRC32 generator; CRC::CRC32 generator;
const uint32_t crc = generator.compute_crc(*contents); const uint32_t crc = generator.compute_crc(*contents);
bool wasUsed = false;
for(const auto &rom: missingRoms) { for(const auto &rom: missingRoms) {
if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) { if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) {
foundROM = true; foundROM = true;
@ -731,10 +741,22 @@ void MainWindow::dropEvent(QDropEvent* event) {
FILE *const target = fopen(destination.c_str(), "wb"); FILE *const target = fopen(destination.c_str(), "wb");
fwrite(contents->data(), 1, contents->size(), target); fwrite(contents->data(), 1, contents->size(), target);
fclose(target); fclose(target);
wasUsed = true;
} }
} }
if(!wasUsed) {
if(!unusedRoms.isEmpty()) unusedRoms += ", ";
unusedRoms += url.fileName();
}
} }
if(!unusedRoms.isEmpty()) {
QMessageBox msgBox;
msgBox.setText("Couldn't identify ROMs: " + unusedRoms);
msgBox.exec();
}
if(foundROM) launchMachine(); if(foundROM) launchMachine();
} break; } break;
} }
@ -1338,24 +1360,25 @@ void MainWindow::addActivityObserver() {
} }
void MainWindow::register_led(const std::string &name) { void MainWindow::register_led(const std::string &name) {
std::lock_guard guard(ledStatusesLock);
ledStatuses[name] = false; ledStatuses[name] = false;
updateStatusBarText(); QMetaObject::invokeMethod(this, "updateStatusBarText");
} }
void MainWindow::set_led_status(const std::string &name, bool isLit) { void MainWindow::set_led_status(const std::string &name, bool isLit) {
std::lock_guard guard(ledStatusesLock);
ledStatuses[name] = isLit; ledStatuses[name] = isLit;
updateStatusBarText(); // Assumption here: Qt's attempt at automatic thread confinement will work here. QMetaObject::invokeMethod(this, "updateStatusBarText");
} }
void MainWindow::updateStatusBarText() { void MainWindow::updateStatusBarText() {
QString fullText; QString fullText;
bool isFirst = true; std::lock_guard guard(ledStatusesLock);
for(const auto &pair: ledStatuses) { for(const auto &pair: ledStatuses) {
if(!isFirst) fullText += " | "; if(!fullText.isEmpty()) fullText += " | ";
fullText += QString::fromStdString(pair.first); fullText += QString::fromStdString(pair.first);
fullText += " "; fullText += " ";
fullText += pair.second ? "" : ""; fullText += pair.second ? "" : "";
isFirst = false;
} }
statusBar()->showMessage(fullText); statusBar()->showMessage(fullText);
} }

View File

@ -5,6 +5,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <memory> #include <memory>
#include <mutex>
#include <optional> #include <optional>
#include "audiobuffer.h" #include "audiobuffer.h"
@ -80,6 +81,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
private slots: private slots:
void startMachine(); void startMachine();
void updateStatusBarText();
private: private:
void start_appleII(); void start_appleII();
@ -100,7 +102,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
QAction *insertAction = nullptr; QAction *insertAction = nullptr;
void insertFile(const QString &fileName); void insertFile(const QString &fileName);
void launchFile(const QString &fileName); bool launchFile(const QString &fileName);
void launchTarget(std::unique_ptr<Analyser::Static::Target> &&); void launchTarget(std::unique_ptr<Analyser::Static::Target> &&);
void restoreSelections(); void restoreSelections();
@ -144,9 +146,11 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void register_led(const std::string &) override; void register_led(const std::string &) override;
void set_led_status(const std::string &, bool) override; void set_led_status(const std::string &, bool) override;
std::recursive_mutex ledStatusesLock;
std::map<std::string, bool> ledStatuses; std::map<std::string, bool> ledStatuses;
void addActivityObserver(); void addActivityObserver();
void updateStatusBarText();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -86,7 +86,7 @@ void ScanTargetWidget::vsync() {
const auto time_now = Time::nanos_now(); const auto time_now = Time::nanos_now();
requestedRedrawTime = vsyncPredictor.suggested_draw_time(); requestedRedrawTime = vsyncPredictor.suggested_draw_time();
const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000; const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000;
if(delay_time > 0) { if(delay_time > 0 && delay_time < vsyncPredictor.frame_duration()) {
QTimer::singleShot(delay_time, this, SLOT(repaint())); QTimer::singleShot(delay_time, this, SLOT(repaint()));
} else { } else {
requestedRedrawTime = 0; requestedRedrawTime = 0;

View File

@ -2,7 +2,7 @@
# Clock Signal # Clock Signal
Clock Signal ('CLK') is an emulator for tourists that seeks to be invisible. Users directly launch classic software with no emulator or per-emulated-machine learning curve. Clock Signal ('CLK') is an emulator for tourists that seeks to be invisible. Users directly launch classic software with no emulator or per-emulated-machine learning curve.
[Releases](https://github.com/TomHarte/CLK/releases) are hosted on GitHub. macOS and source releases are [hosted on GitHub](https://github.com/TomHarte/CLK/releases). For desktop Linux it is also available as a [Snap](https://snapcraft.io/clock-signal).
On the Mac it is a native Cocoa application; under Linux, BSD and other UNIXes and UNIX-alikes it can be built either with Qt or with SDL; the Qt build should be considered preliminary and is currently closely bound to X11 as Qt doesn't abstract game-like keyboard handling. On the Mac it is a native Cocoa application; under Linux, BSD and other UNIXes and UNIX-alikes it can be built either with Qt or with SDL; the Qt build should be considered preliminary and is currently closely bound to X11 as Qt doesn't abstract game-like keyboard handling.