diff --git a/Concurrency/BestEffortUpdater.cpp b/Concurrency/BestEffortUpdater.cpp index fe68c58f8..6eb3635a2 100644 --- a/Concurrency/BestEffortUpdater.cpp +++ b/Concurrency/BestEffortUpdater.cpp @@ -36,7 +36,7 @@ void BestEffortUpdater::update(int flags) { has_skipped_ = update_requested_; update_requested_ = true; flags_ |= flags; - target_time_ = std::chrono::high_resolution_clock::now(); + target_time_ = std::chrono::high_resolution_clock::now().time_since_epoch().count(); } update_condition_.notify_one(); } @@ -71,21 +71,17 @@ void BestEffortUpdater::update_loop() { flags_ = 0; lock.unlock(); - // Calculate period from previous time to now. - const auto elapsed = target_time - previous_time_point_; - previous_time_point_ = target_time; - // Invoke the delegate, if supplied, in order to run. - const int64_t integer_duration = std::chrono::duration_cast(elapsed).count(); - if(integer_duration > 0) { - const auto delegate = delegate_.load(); - if(delegate) { - // Cap running at 1/5th of a second, to avoid doing a huge amount of work after any - // brief system interruption. - const double duration = std::min(double(integer_duration) / 1e9, 0.2); - delegate->update(this, duration, has_skipped_, flags); - has_skipped_ = false; - } + const int64_t integer_duration = std::max(target_time - previous_time_point_, int64_t(0)); + const auto delegate = delegate_.load(); + if(delegate) { + // Cap running at 1/5th of a second, to avoid doing a huge amount of work after any + // brief system interruption. + const double duration = std::min(double(integer_duration) / 1e9, 0.2); + const double elapsed_duraation = delegate->update(this, duration, has_skipped_, flags); + + previous_time_point_ += int64_t(elapsed_duraation * 1e9); + has_skipped_ = false; } } } diff --git a/Concurrency/BestEffortUpdater.hpp b/Concurrency/BestEffortUpdater.hpp index 8626d283f..edf92edb7 100644 --- a/Concurrency/BestEffortUpdater.hpp +++ b/Concurrency/BestEffortUpdater.hpp @@ -33,7 +33,13 @@ class BestEffortUpdater { /// A delegate receives timing cues. struct Delegate { - virtual void update(BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) = 0; + /*! + Instructs the delegate to run for at least @c duration, providing hints as to whether multiple updates were requested before the previous had completed + (as @c did_skip_previous_update) and providing the union of any flags supplied to @c update. + + @returns The amount of time actually run for. + */ + virtual Time::Seconds update(BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) = 0; }; /// Sets the current delegate. @@ -52,13 +58,13 @@ class BestEffortUpdater { std::atomic should_quit_; std::atomic is_updating_; - std::chrono::time_point target_time_; + int64_t target_time_; int flags_ = 0; bool update_requested_; std::mutex update_mutex_; std::condition_variable update_condition_; - std::chrono::time_point previous_time_point_; + decltype(target_time_) previous_time_point_; bool has_previous_time_point_ = false; std::atomic has_skipped_ = false; diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 098a3c230..da82b2e3d 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -243,7 +243,7 @@ class MachineDocument: /// Responds to the CSAudioQueueDelegate dry-queue warning message by requesting a machine update. final func audioQueueIsRunningDry(_ audioQueue: CSAudioQueue) { bestEffortLock.lock() - bestEffortUpdater?.update() + bestEffortUpdater?.update(with: .audioNeeded) bestEffortLock.unlock() } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index d2d2b1ebb..83f6d072b 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -57,7 +57,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { */ - (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray *)missingROMs NS_DESIGNATED_INITIALIZER; -- (void)runForInterval:(NSTimeInterval)interval untilEvent:(int)events; +- (NSTimeInterval)runForInterval:(NSTimeInterval)interval untilEvent:(int)events; - (float)idealSamplingRateFromRange:(NSRange)range; - (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index c32e52f81..dbde81399 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -259,7 +259,7 @@ struct ActivityObserver: public Activity::Observer { } } -- (void)runForInterval:(NSTimeInterval)interval untilEvent:(int)events { +- (NSTimeInterval)runForInterval:(NSTimeInterval)interval untilEvent:(int)events { @synchronized(self) { if(_joystickMachine && _joystickManager) { [_joystickManager update]; @@ -309,7 +309,7 @@ struct ActivityObserver: public Activity::Observer { } } } - _machine->crt_machine()->run_until(interval, events); + return _machine->crt_machine()->run_until(interval, events); } } diff --git a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h index 24e22c088..970104107 100644 --- a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h +++ b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h @@ -11,9 +11,16 @@ #import "CSMachine.h" +// The following is coupled to the definitions in CRTMachine.hpp, but exposed here +// for the benefit of Swift. +typedef NS_ENUM(NSInteger, CSBestEffortUpdaterEvent) { + CSBestEffortUpdaterEventAudioNeeded = 1 << 0 +}; + @interface CSBestEffortUpdater : NSObject - (void)update; +- (void)updateWithEvent:(CSBestEffortUpdaterEvent)event; - (void)flush; - (void)setMachine:(CSMachine *)machine; diff --git a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm index 3c4142fbf..a0ba3e21f 100644 --- a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm +++ b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm @@ -13,8 +13,8 @@ struct UpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate { __weak CSMachine *machine; - void update(Concurrency::BestEffortUpdater *updater, Time::Seconds seconds, bool did_skip_previous_update, int flags) { - [machine runForInterval:seconds untilEvent:flags]; + Time::Seconds update(Concurrency::BestEffortUpdater *updater, Time::Seconds seconds, bool did_skip_previous_update, int flags) final { + return [machine runForInterval:seconds untilEvent:flags]; } }; @@ -35,6 +35,10 @@ struct UpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate { _updater.update(); } +- (void)updateWithEvent:(CSBestEffortUpdaterEvent)event { + _updater.update((int)event); +} + - (void)flush { _updater.flush(); } diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index dab9b9159..6a70d98dd 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -34,8 +34,8 @@ namespace { struct BestEffortUpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate { - void update(Concurrency::BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) override { - machine->crt_machine()->run_until(duration, flags); + Time::Seconds update(Concurrency::BestEffortUpdater *updater, Time::Seconds duration, bool did_skip_previous_update, int flags) override { + return machine->crt_machine()->run_until(duration, flags); } Machine::DynamicMachine *machine;