1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-24 12:30:17 +00:00

Hacks up an [unsafe] return to something best-effort-updater-esque.

For profiling, mainly.
This commit is contained in:
Thomas Harte 2022-07-07 16:35:45 -04:00
parent 01a309909b
commit 3e2a6ef3f4
4 changed files with 54 additions and 21 deletions

View File

@ -20,6 +20,11 @@ inline Nanos nanos_now() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
} }
inline Seconds seconds(Nanos nanos) {
return double(nanos) / 1e9;
}
} }
#endif /* TimeTypes_h */ #endif /* TimeTypes_h */

View File

@ -20,7 +20,7 @@ namespace Concurrency {
template <typename Performer> class AsyncUpdater { template <typename Performer> class AsyncUpdater {
public: public:
template <typename... Args> AsyncUpdater(Args&&... args) : template <typename... Args> AsyncUpdater(Args&&... args) :
performer_(std::forward<Args>(args)...), performer(std::forward<Args>(args)...),
actions_(std::make_unique<ActionVector>()), actions_(std::make_unique<ActionVector>()),
performer_thread_{ performer_thread_{
[this] { [this] {
@ -30,7 +30,7 @@ template <typename Performer> class AsyncUpdater {
while(!should_quit) { while(!should_quit) {
// Wait for new actions to be signalled, and grab them. // Wait for new actions to be signalled, and grab them.
std::unique_lock lock(condition_mutex_); std::unique_lock lock(condition_mutex_);
while(!actions_) { while(actions_->empty()) {
condition_.wait(lock); condition_.wait(lock);
} }
std::swap(actions, actions_); std::swap(actions, actions_);
@ -38,7 +38,7 @@ template <typename Performer> class AsyncUpdater {
// Update to now. // Update to now.
auto time_now = Time::nanos_now(); auto time_now = Time::nanos_now();
performer_.perform(time_now - last_fired); performer.perform(time_now - last_fired);
last_fired = time_now; last_fired = time_now;
// Perform the actions. // Perform the actions.
@ -68,10 +68,10 @@ template <typename Performer> class AsyncUpdater {
performer_thread_.join(); performer_thread_.join();
} }
private:
// The object that will actually receive time advances. // The object that will actually receive time advances.
Performer performer_; Performer performer;
private:
// The list of actions waiting be performed. These will be elided, // The list of actions waiting be performed. These will be elided,
// increasing their latency, if the emulation thread falls behind. // increasing their latency, if the emulation thread falls behind.
using ActionVector = std::vector<std::function<void(void)>>; using ActionVector = std::vector<std::function<void(void)>>;

View File

@ -15,7 +15,6 @@ class MachineDocument:
NSWindowDelegate, NSWindowDelegate,
CSMachineDelegate, CSMachineDelegate,
CSScanTargetViewResponderDelegate, CSScanTargetViewResponderDelegate,
CSAudioQueueDelegate,
CSROMReciverViewDelegate CSROMReciverViewDelegate
{ {
// MARK: - Mutual Exclusion. // MARK: - Mutual Exclusion.
@ -274,17 +273,12 @@ class MachineDocument:
if self.audioQueue == nil || self.audioQueue.samplingRate != selectedSamplingRate || self.audioQueue != self.machine.audioQueue { if self.audioQueue == nil || self.audioQueue.samplingRate != selectedSamplingRate || self.audioQueue != self.machine.audioQueue {
self.machine.audioQueue = nil self.machine.audioQueue = nil
self.audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate), isStereo:isStereo) self.audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate), isStereo:isStereo)
self.audioQueue.delegate = self
self.machine.audioQueue = self.audioQueue self.machine.audioQueue = self.audioQueue
self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo) self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo)
} }
} }
} }
/// Responds to the CSAudioQueueDelegate dry-queue warning message by requesting a machine update.
final func audioQueueIsRunningDry(_ audioQueue: CSAudioQueue) {
}
// MARK: - Pasteboard Forwarding. // MARK: - Pasteboard Forwarding.
/// Forwards any text currently on the pasteboard into the active machine. /// Forwards any text currently on the pasteboard into the active machine.

View File

@ -37,9 +37,16 @@
namespace { namespace {
struct Updater {}; struct MachineUpdater {
void perform(Time::Nanos duration) {
// Top out at 0.1 seconds; this is a safeguard against a negative
// feedback loop if emulation starts running slowly.
const auto seconds = std::min(Time::seconds(duration), 0.1);
machine->run_for(seconds);
}
Concurrency::AsyncUpdater<Updater> updater(); MachineTypes::TimedMachine *machine = nullptr;
};
} }
@ -49,6 +56,10 @@ Concurrency::AsyncUpdater<Updater> updater();
- (void)addLED:(NSString *)led isPersistent:(BOOL)isPersistent; - (void)addLED:(NSString *)led isPersistent:(BOOL)isPersistent;
@end @end
@interface CSMachine() <CSAudioQueueDelegate>
- (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue;
@end
struct LockProtectedDelegate { struct LockProtectedDelegate {
// Contractual promise is: machine, the pointer **and** the object **, may be accessed only // Contractual promise is: machine, the pointer **and** the object **, may be accessed only
// in sections protected by the machineAccessLock; // in sections protected by the machineAccessLock;
@ -106,6 +117,7 @@ struct ActivityObserver: public Activity::Observer {
CSStaticAnalyser *_analyser; CSStaticAnalyser *_analyser;
std::unique_ptr<Machine::DynamicMachine> _machine; std::unique_ptr<Machine::DynamicMachine> _machine;
MachineTypes::JoystickMachine *_joystickMachine; MachineTypes::JoystickMachine *_joystickMachine;
Concurrency::AsyncUpdater<MachineUpdater> updater;
CSJoystickManager *_joystickManager; CSJoystickManager *_joystickManager;
NSMutableArray<CSMachineLED *> *_leds; NSMutableArray<CSMachineLED *> *_leds;
@ -142,6 +154,7 @@ struct ActivityObserver: public Activity::Observer {
[missingROMs appendString:[NSString stringWithUTF8String:wstring_converter.to_bytes(description).c_str()]]; [missingROMs appendString:[NSString stringWithUTF8String:wstring_converter.to_bytes(description).c_str()]];
return nil; return nil;
} }
updater.performer.machine = _machine->timed_machine();
// Use the keyboard as a joystick if the machine has no keyboard, or if it has a 'non-exclusive' keyboard. // Use the keyboard as a joystick if the machine has no keyboard, or if it has a 'non-exclusive' keyboard.
_inputMode = _inputMode =
@ -219,6 +232,11 @@ struct ActivityObserver: public Activity::Observer {
} }
} }
- (void)setAudioQueue:(CSAudioQueue *)audioQueue {
_audioQueue = audioQueue;
audioQueue.delegate = self;
}
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize stereo:(BOOL)stereo { - (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize stereo:(BOOL)stereo {
@synchronized(self) { @synchronized(self) {
[self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize stereo:stereo]; [self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize stereo:stereo];
@ -442,9 +460,12 @@ struct ActivityObserver: public Activity::Observer {
} }
- (void)applyInputEvent:(dispatch_block_t)event { - (void)applyInputEvent:(dispatch_block_t)event {
@synchronized(_inputEvents) { updater.update([event] {
[_inputEvents addObject:event]; event();
} });
// @synchronized(_inputEvents) {
// [_inputEvents addObject:event];
// }
} }
- (void)clearAllKeys { - (void)clearAllKeys {
@ -655,9 +676,21 @@ struct ActivityObserver: public Activity::Observer {
#pragma mark - Timer #pragma mark - Timer
- (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue {
// TODO: Make audio flushes overt, and do one here.
updater.update([] {});
}
- (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime { - (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime {
updater.update([self] {
dispatch_async(dispatch_get_main_queue(), ^{
[self.view updateBacking];
[self.view draw];
});
});
// First order of business: grab a timestamp. // First order of business: grab a timestamp.
const auto timeNow = Time::nanos_now(); /* const auto timeNow = Time::nanos_now();
BOOL isSyncLocking; BOOL isSyncLocking;
@synchronized(self) { @synchronized(self) {
@ -682,13 +715,14 @@ struct ActivityObserver: public Activity::Observer {
// Draw the current output. (TODO: do this within the timer if either raster racing or, at least, sync matching). // Draw the current output. (TODO: do this within the timer if either raster racing or, at least, sync matching).
if(!isSyncLocking) { if(!isSyncLocking) {
[self.view draw]; [self.view draw];
} }*/
} }
#define TICKS 1000 #define TICKS 120
- (void)start { - (void)start {
__block auto lastTime = Time::nanos_now(); // updater.performer.machine = _machine->timed_machine();
/* __block auto lastTime = Time::nanos_now();
_timer = [[CSHighPrecisionTimer alloc] initWithTask:^{ _timer = [[CSHighPrecisionTimer alloc] initWithTask:^{
// Grab the time now and, therefore, the amount of time since the timer last fired // Grab the time now and, therefore, the amount of time since the timer last fired
@ -762,7 +796,7 @@ struct ActivityObserver: public Activity::Observer {
} }
lastTime = timeNow; lastTime = timeNow;
} interval:uint64_t(1000000000) / uint64_t(TICKS)]; } interval:uint64_t(1000000000) / uint64_t(TICKS)];*/
} }
#undef TICKS #undef TICKS